UI_CUSTOM_DRAW_CALLBACK(BuildNavViewDrawCallback)
{
	scene_view *SceneView = (scene_view *)Data;
	SV_DrawBackground(SceneView, Box, Group);
}

static void W_NavEditorSerializeItems(workspace_view_nav_editor *Editor)
{
	temp Scratch = GetScratch();
	
	//- sixten: find total size
	u64 TotalSize = sizeof(u16); // item count
	for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next)
	{
		string AssetName = MakeString(AssetNameLUT[Node->Item.TextureID]);
		
		TotalSize += 1; TotalSize += sizeof(u16); TotalSize += AssetName.Count;                                          // texture id
		TotalSize += 1; TotalSize += sizeof(r32);                                                                        // scale
		TotalSize += 1; TotalSize += sizeof(v2_r32);                                                                     // origin
		TotalSize += 1; TotalSize += sizeof(v2_r32);                                                                     // p
		TotalSize += 1; TotalSize += sizeof(u16); TotalSize += Node->Item.HoverText.Count;                               // hover text
		TotalSize += 1; TotalSize += sizeof(u16); TotalSize += sizeof(u8); TotalSize += Node->Item.Action.Content.Count; // action
		TotalSize += 1;                                                                                                  // end of item
	}
	
	u8 *FileData = PushArray(Scratch.Arena, u8, TotalSize);
	u8 *Data = FileData;
	
	*(u16 *)Data = Editor->Items.Count;
	Data += sizeof(u16);
	
#define WriteByte(Op)                do { *Data++ = Op; } while(0)
#define WriteField(Op, type, Member) do { *Data++ = Op; *(type *)Data = Member; Data += sizeof(type); } while(0)
#define WriteString(String)          do { *(u16 *)Data = (u16)String.Count; Data += sizeof(u16); Copy(Data, String.Data, String.Count); Data += String.Count; } while(0)
	
	for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next)
	{
		scene_nav_item *Item = &Node->Item;
		string AssetName = MakeString(AssetNameLUT[Node->Item.TextureID]);
		WriteByte(S_NavItemOp_TextureID); WriteString(AssetName);
		WriteField(S_NavItemOp_Scale,     r32,      Item->Scale);
		WriteField(S_NavItemOp_Origin,    v2_r32,   Item->Origin);
		WriteField(S_NavItemOp_P,         v2_r32,   Item->P);
		WriteByte(S_NavItemOp_HoverText); WriteString(Item->HoverText);
		WriteByte(S_NavItemOp_Action); WriteByte(Item->Action.Kind); WriteString(Item->Action.Content);
		WriteByte(S_NavItemOp_None);
	}
#undef WriteByte
#undef WriteField
#undef WriteString
	
	platform_file_handle File = Platform.OpenFile(PushFormat(Scratch.Arena, "%S/%S", Editor->FilePath, Editor->FileName), PlatformAccess_Write);
	if(File.IsValid)
	{
		Platform.WriteFile(File, FileData, 0, TotalSize);
		Platform.CloseFile(File);
	}
	ReleaseScratch(Scratch);
}

static workspace_string_chunk *W_StringChunkAlloc(arena *Arena, workspace_string_chunk_list *FreeList)
{
	workspace_string_chunk *Result = FreeList->First;
	if(Result)
	{
		DLLRemove(FreeList->First, FreeList->Last, Result);
		*Result = {};
	}
	else
	{
		Result = PushStruct(Arena, workspace_string_chunk);
	}
	
	return(Result);
}

static void W_StringChunkRelease(workspace_string_chunk_list *FreeList, workspace_string_chunk *Chunk)
{
	DLLInsertLast(FreeList->First, FreeList->Last, Chunk);
}

static scene_nav_item_node *W_SceneNavItemNodeAlloc(arena *Arena, workspace_view_nav_editor *Editor)
{
	scene_nav_item_node *Result = Editor->FirstFree;
	if(Result)
	{
		DLLRemove(Editor->FirstFree, Editor->LastFree, Result);
		*Result = {};
	}
	else
	{
		Result = PushStruct(Arena, scene_nav_item_node);
	}
	
	Result->HoverTextStringChunk =  W_StringChunkAlloc(Arena, &Editor->FreeStrings);
	Result->ActionStringChunk =  W_StringChunkAlloc(Arena, &Editor->FreeStrings);
	
	Result->Item.HoverText.Data = Result->HoverTextStringChunk->Data;
	Result->Item.Action.Content.Data = Result->ActionStringChunk->Data;
	
	DLLInsertLast(Editor->Items.First, Editor->Items.Last, Result);
	Editor->Items.Count += 1;
	return(Result);
}

static void W_SceneNavItemNodeRelease(workspace_view_nav_editor *Editor, scene_nav_item_node *Node)
{
	W_StringChunkRelease(&Editor->FreeStrings, Node->HoverTextStringChunk);
	W_StringChunkRelease(&Editor->FreeStrings, Node->ActionStringChunk);
	DLLRemove(Editor->Items.First, Editor->Items.Last, Node);
	DLLInsertLast(Editor->FirstFree, Editor->LastFree, Node);
	Editor->Items.Count -= 1;
}

static void W_NavEditorInspectNode(workspace_view_nav_editor *Editor, scene_nav_item_node *Node)
{
	Editor->SelectedItem = Node;
	Editor->TextureDropdownOpen = false;
	Editor->NavActionDropdownOpen = false;
}

static void W_NavEditorSetup(workspace_view *View, string FilePath, string FileName, string FileContents)
{
	workspace_view_nav_editor *Editor = (workspace_view_nav_editor *)View->Data;
	
	//- sixten: setup file info
	Editor->FileName = PushString(View->Arena, FileName);
	Editor->FilePath = PushString(View->Arena, FilePath);
	
	//- sixten: deserialize data
	if(FileContents.Count != 0)
	{
		u8 *DataBegin = FileContents.Data;
		u8 *Byte = DataBegin;
		
		u16 ItemCount = *(u16 *)Byte;
		Byte += 2;
		
		//- sixten: parse items
		for(u64 ItemIndex = 0; ItemIndex < ItemCount; ItemIndex += 1)
		{
			scene_nav_item_node *Node = W_SceneNavItemNodeAlloc(View->Arena, Editor);
			scene_nav_item *Item = &Node->Item;
			for(;;)
			{
				switch(*Byte++)
				{
					case S_NavItemOp_TextureID:
					{
						string AssetName;
						AssetName.Count = *(u16 *)Byte;
						Byte += sizeof(u16);
						AssetName.Data = Byte;
						Byte += AssetName.Count;
						
						Item->TextureID = AssetID_Error;
						for(u64 AssetIndex = 0; AssetIndex < AssetID_COUNT; AssetIndex += 1)
						{
							if(AreEqual(MakeString(AssetNameLUT[AssetIndex]), AssetName))
							{
								Item->TextureID = AssetIndex;
								break;
							}
						}
					} goto Next;
					case S_NavItemOp_Scale:
					{
						Item->Scale = *(r32 *)Byte;
						Byte += sizeof(r32);
					} goto Next;
					case S_NavItemOp_Origin:
					{
						Item->Origin = *(v2_r32 *)Byte;
						Byte += sizeof(v2_r32);
					} goto Next;
					case S_NavItemOp_P:
					{
						Item->P = *(v2_r32 *)Byte;
						Byte += sizeof(v2_r32);
					} goto Next;
					case S_NavItemOp_HoverText:
					{
						Item->HoverText.Count = *(u16 *)Byte;
						Byte += 2;
						Assert(Item->HoverText.Count <= W_STRING_CHUNK_SIZE-2*sizeof(void *));
						
						Copy(Item->HoverText.Data, Byte, Item->HoverText.Count);
						Byte += Item->HoverText.Count;
					} goto Next;
					case S_NavItemOp_Action:
					{
						Item->Action.Kind = (scene_nav_action_kind)*Byte;
						Byte += 1;
						
						Item->Action.Content.Count = *(u16 *)Byte;
						Byte += 2;
						Assert(Item->Action.Content.Count <= W_STRING_CHUNK_SIZE-2*sizeof(void *));
						
						Copy(Item->Action.Content.Data, Byte, Item->Action.Content.Count);
						Byte += Item->Action.Content.Count;
					} goto Next;
				}
				
				//- sixten: no op found, assume item done
				break;
				
				Next:;
			}
		}
	}
}

static void W_BuildNavEditor(workspace_view *View)
{
	workspace_view_nav_editor *Editor = (workspace_view_nav_editor *)View->Data;
	scene_view *SceneView = SV_GetState();
	
	UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1))
		UI_BorderColor(V4(0.45, 0.45, 0.45, 1))
		UI_WidthFill UI_Height(UI_Em(2.0f, 1.0f))
		UI_LayoutAxis(Axis2_X)
		UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, "Workspace Nav Editor Toolbar"))
	{
		UI_Width(UI_Em(2.0f, 1.0f)) UI_Font(Font_Icons) UI_CornerRadius(4.0f)
		{
			//- sixten: create nav item button
			if(UI_ButtonF("%U", FontIcon_UserPlus).Clicked)
			{
				scene_nav_item_node *Node = W_SceneNavItemNodeAlloc(View->Arena, Editor);
				
				//- sixten: apply default options
				Node->Item.Scale = 0.01f;
				Node->Item.Origin = V2R32(0.5, 1.0f);
				Node->Item.P = V2R32(0.0, 0.0f);
				Node->Item.TextureID = AssetID_ArthurNormal;
			}
			
			//- sixten: create save nav items button
			if(UI_ButtonF("%U", FontIcon_Floppy).Clicked)
			{
				W_NavEditorSerializeItems(Editor);
			}
			
			//- sixten: create force reload
			if(UI_ButtonF("%U", FontIcon_ArrowsCW).Clicked)
			{
				SV_LoadNavItems();
			}
		}
	}
	
	UI_Row()
	{
		//- sixten: build inspector panel
		scene_nav_item_node *SelectedItem = Editor->SelectedItem;
		UI_SetNextLayoutAxis(Axis2_X);
		UI_Width(UI_Em(24.0f*AC_AnimateValueF(SelectedItem != 0, 0, 0.3f, "Workspace Nav Editor Inspector %p", Editor), 1.0f)) UI_HeightFill
			UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder, StrLit("Workspace Nav Editor Inspector")))
			UI_Padding(UI_Em(1, 1)) UI_Width(UI_Percent(1.0f, 0.0f)) UI_Column()
		{
			if(SelectedItem)
			{
				UI_CornerRadius(4.0f) UI_Width(UI_Percent(1, 1)) UI_Height(UI_TextContent(15, 1))
				{
					//- sixten: inspect position
					UI_Row() UI_Width(UI_Percent(0.5f, 1.0f)) {UI_LabelF("Position X:"); UI_Font(Font_Monospace) UI_LabelF("%f", SelectedItem->Item.P.x);}
					UI_Row() UI_Width(UI_Percent(0.5f, 1.0f)) {UI_LabelF("Position Y:"); UI_Font(Font_Monospace) UI_LabelF("%f", SelectedItem->Item.P.y);}
					
					//- sixten: inspect scale
					UI_Row() UI_Width(UI_Percent(0.5f, 1.0f))
					{
						UI_LabelF("Scale:");
						UI_Font(Font_Monospace)
						{
							UI_SetNextHoverCursor(PlatformCursor_ArrowHorizontal);
							ui_box *ScaleBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, "%f##Scale", SelectedItem->Item.Scale);
							
							ui_signal ScaleSignal = UI_SignalFromBox(ScaleBox);
							if(ScaleSignal.Dragging)
							{
								if(ScaleSignal.Pressed)
								{
									UI_StoreDragR32(SelectedItem->Item.Scale);
								}
								
								SelectedItem->Item.Scale = UI_GetDragR32() + ScaleSignal.DragDelta.x*0.001f;
							}
						}
					}
					
					//- sixten: inspect texture
					UI_Row(0, StrLit("Texture")) UI_Width(UI_Percent(0.5f, 1.0f))
					{
						UI_LabelF("Texture:");
						UI_DropdownSelection(AssetNameLUT, ArrayCount(AssetNameLUT), &Editor->TextureDropdownOpen, &SelectedItem->Item.TextureID);
					}
					
					//- sixten: inspect nav action kind
					UI_Row(0, StrLit("Nav Action")) UI_Width(UI_Percent(0.5f, 1.0f))
					{
						UI_LabelF("Nav Action Type:");
						char *Actions[] = { "None", "Trigger Proc", "Change Scene" };
						UI_DropdownSelection(Actions, ArrayCount(Actions), &Editor->NavActionDropdownOpen, (s32 *)&SelectedItem->Item.Action);
					}
					
					//- sixten: inspect nav action contents
					UI_Row(0, StrLit("Nav Action String")) UI_Width(UI_Percent(0.5f, 1.0f))
					{
						UI_LabelF("Nav Action Contents:");
						ui_signal Signal = UI_LineEdit(&Editor->NavActionStringEditState, ArrayCount(SelectedItem->ActionStringChunk->Data), SelectedItem->ActionStringChunk->Data, &SelectedItem->Item.Action.Content.Count, StrLit("Nav Action Text"), Editor->ActiveTextThing == 1);
						if(AreEqual(UI_ActiveKey(), Signal.Box->Key))
						{
							Editor->ActiveTextThing = 1;
						}
						else if(!AreEqual(UI_ActiveKey(), UI_EmptyKey()) && Editor->ActiveTextThing == 1)
						{
							Editor->ActiveTextThing = 0;
						}
					}
					
					//- sixten: inspect hover text
					UI_Row(0, StrLit("Hover Text")) UI_Width(UI_Percent(0.5f, 1.0f))
					{
						UI_LabelF("Hover Text:");
						ui_signal Signal = UI_LineEdit(&Editor->HoverTextEditState, ArrayCount(SelectedItem->HoverTextStringChunk->Data), SelectedItem->HoverTextStringChunk->Data, &SelectedItem->Item.HoverText.Count, StrLit("Hover Text Input"), Editor->ActiveTextThing == 2);
						if(AreEqual(UI_ActiveKey(), Signal.Box->Key))
						{
							Editor->ActiveTextThing = 2;
						}
						else if(!AreEqual(UI_ActiveKey(), UI_EmptyKey()) && Editor->ActiveTextThing == 2)
						{
							Editor->ActiveTextThing = 0;
						}
					}
					
					UI_Spacer(UI_TextContent(15, 1));
					
					UI_Row() UI_FillPadding
					{
						UI_Width(UI_TextContent(15, 1)) if(UI_ButtonF("Remove Item").Pressed)
						{
							W_SceneNavItemNodeRelease(Editor, SelectedItem);
							Editor->SelectedItem = 0;
						}
					}
				}
			}
		}
		
		//- sixten: build nav view
		UI_Width(UI_Percent(1, 0)) UI_Height(UI_Percent(1, 0)) UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip, "Workspace View Nav Editor Thing"))
		{
			//- sixten: calculate nav view size
			v2_r32 ParentDim = DimOfRange(UI_TopParent()->Rect);
			v2_r32 BoxDim = ParentDim;
			
			r32 TargetRatio = 16.0f/9.0f;
			r32 ActualRatio = ParentDim.x/ParentDim.y;
			
			if(ActualRatio>TargetRatio)
			{
				BoxDim.x = BoxDim.y*TargetRatio;
			}
			else
			{
				BoxDim.y = BoxDim.x/TargetRatio;
			}
			
			UI_SetNextWidth(UI_Pixels(BoxDim.x, 1));
			UI_SetNextHeight(UI_Pixels(BoxDim.y, 1));
			UI_SetNextLayoutAxis(Axis2_Y);
			UI_SetNextFixedP((ParentDim-BoxDim)*0.5f);
			ui_box *Box = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_DrawDropShadow|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|UI_BoxFlag_Clickable, "Nav View %p", View);
			UI_EquipBoxCustomDrawCallback(Box, BuildNavViewDrawCallback, SceneView);
			
			r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim);
			
			//- sixten: build all nav items
			s32 ItemIndex = 0;
			UI_Parent(Box) for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next, ItemIndex += 1)
			{
				scene_nav_item *Item = &Node->Item;
				
				//- sixten: calculate item position
				r32 AppliedScale = GlobalScale*Item->Scale;
				render_handle Texture = TextureFromAssetID(Item->TextureID);
				v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture));
				v2_r32 TextureOrigin = Hadamard(TextureDim, Item->Origin);
				v2_r32 Dim = TextureDim*AppliedScale;
				v2_r32 OriginP = TextureOrigin*AppliedScale;
				
				//- sixten: build the item
				scene_nav_item_info *Data = PushStruct(UI_FrameArena(), scene_nav_item_info);
				Data->Item = Item;
				
				UI_SetNextHoverCursor(PlatformCursor_Hand);
				UI_SetNextSize(UI_Pixels(Dim.x, 1), UI_Pixels(Dim.y, 1));
				ui_box *ItemBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|((Node==Editor->SelectedItem)?UI_BoxFlag_DrawBorder:0),
																																		"View Item Box %i", ItemIndex);
				UI_EquipBoxCustomDrawCallback(ItemBox, BuildNavItemDrawCallback, Data);
				
				if(Node == Editor->SelectedItem)
				{
					// sixten(TODO): this:
					//ui_box *OriginBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, "View Item Origin Box %i", ItemIndex);
				}
				//- sixten: handle signals
				{
					ui_signal Signal = UI_SignalFromBox(ItemBox);
					Data->Signal = Signal;
					
					//- sixten: inspect pressed item
					if(Signal.Clicked && Signal.DragDelta == V2R32(0, 0))
					{
						W_NavEditorInspectNode(Editor, Node);
					}
					
					//- sixten: handle dragging
					if(Signal.Dragging)
					{
						if(Signal.Pressed)
						{
							UI_StoreDragV2(Item->P);
						}
						
						Item->P = UI_GetDragV2() + Signal.DragDelta/BoxDim*2;
					}
				}
				
				//- sixten: apply the calculated position
				v2_r32 OffsetP = BoxDim*(V2R32(1, 1) + Item->P)*0.5f;
				ItemBox->FixedP = (OffsetP-OriginP);
			}
			
			ui_signal BackgroundSignal = UI_SignalFromBox(Box);
			if(BackgroundSignal.Pressed)
			{
				W_NavEditorInspectNode(Editor, 0);
			}
		}
	}
}