#include "vn_platform.h"
#include "vn_platform.cpp"

#define STB_IMAGE_IMPLEMENTATION
#include "third_party/stb_image.h"

#if VN_INTERNAL
struct debug_settings
{
	b32 RenderUIDebugRects;
	b32 RenderFPSCounter;
	b32 ListHotAndActive;
	b32 ShowWelcomeMessage;
};

global debug_settings *DEBUG_DebugSettings = 0;
#endif

#include "generated/vn_character.meta.h"

#include "vn_asset.h"
#include "vn_tokenizer.h"
#include "vn_config.h"
#include "vn_font.h"
#include "vn_text_op.h"
#include "vn_ui.h"
#include "vn_ui_utils.h"
#include "vn_scene2.h"
#include "vn_scene_view.h"
#include "vn_animation_curve.h"
#include "vn_theme_dark.h"

#if VN_INTERNAL
#include "vn_workspace.h"
#include "vn_debug_info.h"

global vn_render_commands *GlobalRenderCommands = 0;
#endif

#include "generated/vn_character.meta.c"

#include "vn_asset.cpp"
#include "vn_tokenizer.cpp"
#include "vn_config.cpp"
#include "vn_render.cpp"
#include "vn_font.cpp"
#include "vn_ui.cpp"
#include "vn_ui_utils.cpp"
#include "vn_animation_curve.cpp"
#include "vn_scene2.cpp"
#include "vn_scene_view.cpp"

#if VN_INTERNAL
#include "vn_workspace.cpp"
#include "vn_debug_info.cpp"
#endif

struct vn_state
{
	arena *Arena;
	arena *FrameArena;
	
	assets Assets;
	
	glyph_atlas *GlyphAtlas;
	
	config *Config;
	
	ui UI;
	animation_curve_state AnimationCurveState;
	b32 EditorMode;
	
	scene_view SceneView;
	
#if VN_INTERNAL
	debug_info *DebugInfo;
	workspace Workspace;
	debug_settings DebugSettings;
#endif
};

VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
{
	SetThreadContext(ThreadContext);
	Platform = Memory->PlatformAPI;
	
#if VN_INTERNAL
	GlobalRenderCommands = RenderCommands;
#endif
	
	vn_state *State = Memory->State;
	
	//- sixten: initialize application state
	if(!Memory->State)
	{
		arena *Arena = ArenaAlloc(Kilobytes(24), true, "VN State Arena");
		State = Memory->State = PushStruct(Arena, vn_state);
		State->Arena = Arena;
		State->FrameArena = ArenaAlloc(Kilobytes(1), true, "VN Frame Arena");
		
		State->GlyphAtlas = F_CreateGlyphAtlas(RenderCommands, 2048, 128);
		State->Config = CreateConfig();
		
		//- sixten: load assets
		State->Assets.AllocateTexture = RenderCommands->AllocateTexture;
		LoadPermanentAssets(&State->Assets);
		
		//- sixten: setup config binds and load current config
		{
			Config_BindS32(State->Config, StrLit("Platform/RefreshRate"),   &Input->RefreshRate, 60);
#if VN_INTERNAL
			State->DebugInfo = DI_DebugInfoAlloc();
			
			DEBUG_DebugSettings = &State->DebugSettings;
			Config_BindB32(State->Config, StrLit("Dev/RenderUIDebugRects"), &DEBUG_DebugSettings->RenderUIDebugRects, 0);
			Config_BindB32(State->Config, StrLit("Dev/RenderFPSCounter"),   &DEBUG_DebugSettings->RenderFPSCounter, 0);
			Config_BindB32(State->Config, StrLit("Dev/ListHotAndActive"),   &DEBUG_DebugSettings->ListHotAndActive, 0);
			Config_BindB32(State->Config, StrLit("Dev/ShowWelcomeMessage"), &DEBUG_DebugSettings->ShowWelcomeMessage, 1);
#endif
			
			Config_ReadFile(State->Config, StrLit("config.vn"));
		}
		
		SV_Init(&State->SceneView);
		UI_Init(&State->UI);
		AC_Init(&State->AnimationCurveState);
		
#if VN_INTERNAL
		W_Init(&State->Workspace);
#endif
        
        
		//- sixten: load startup scene
		temp Scratch = GetScratch();
		compiled_scene2 Scene = S2_CompiledFromString(Scratch.Arena, Platform_ReadEntireFile(Scratch.Arena, StrLit("data/omori.vns")));
		SV_SetCurrentSource(&Scene);
		ReleaseScratch(Scratch);
		
		return;
	}
	
#if VN_INTERNAL
	DI_BeginFrame(State->DebugInfo);
	DEBUG_DebugSettings = &State->DebugSettings;
#endif
	
	//- sixten: begin new frame
	ArenaClear(State->FrameArena);
	SetAssets(&State->Assets);
	AC_NewFrame(&State->AnimationCurveState, Input->dtForFrame);
	UI_NewFrame(&State->UI, Input->EventList, Input->MouseP, Input->dtForFrame, State->GlyphAtlas);
	SV_NewFrame(&State->SceneView, Input->EventList, Input->dtForFrame);
	
	//- sixten: check for toggle between modes
	if(Platform_KeyPress(Input->EventList, Key_Return, PlatformModifier_Ctrl))
	{
		State->EditorMode = !State->EditorMode;
	}
	
	//- sixten: update the scene
	SV_Update(State->FrameArena);
	
	//- sixten: build the ui
	UI_BeginBuild(RenderCommands->RenderDim);
	{
#if VN_INTERNAL
		DI_BuildInfo();
		if(State->EditorMode)
		{
			W_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas);
		}
		else
		{
#endif
			SV_BuildSceneView(Input);
#if VN_INTERNAL
		}
#endif
	}
	UI_EndBuild();
	
	
	//- sixten: consume all remaining evetns
	for(platform_event *Event = Input->EventList->First;
        Event != 0;
        Event = Event->Next)
	{
		if(Event->Type == PlatformEvent_WindowClose)
		{
			Config_WriteFile(State->Config, StrLit("config.vn"));
			Input->ExitRequested = true;
		}
		
		Platform_ConsumeEvent(Input->EventList, Event);
	}
	
	//- sixten: render the frame
	{
		render_group Group = BeginRenderGroup(RenderCommands);
		PushClear(&Group, V3R32(0.1, 0.1, 0.1));
		
		UI_RenderFrame(&Group, State->GlyphAtlas);
		
#if VN_INTERNAL
        r32 DEBUGDisplayOffsetY = 20;
		if(DEBUG_DebugSettings->ListHotAndActive)
		{
			PushText(&Group, State->GlyphAtlas, Font_Regular, V2R32(5, RenderCommands->RenderDim.y - DEBUGDisplayOffsetY), 15, Color_Grey,
                     PushFormat(State->UI.FrameArena, "Hot: %S:%llu", UI_BoxStringFromKey(UI_HotKey()), UI_HotKey()));
            DEBUGDisplayOffsetY += 20;
			PushText(&Group, State->GlyphAtlas, Font_Regular, V2R32(5, RenderCommands->RenderDim.y - DEBUGDisplayOffsetY), 15, Color_Grey,
                     PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_BoxStringFromKey(UI_ActiveKey()), UI_ActiveKey()));
            DEBUGDisplayOffsetY += 20;
		}
        
        if(DEBUG_DebugSettings->RenderFPSCounter)
        {
            PushText(&Group, State->GlyphAtlas, Font_Regular, V2R32(5, RenderCommands->RenderDim.y - DEBUGDisplayOffsetY), 15, Color_Grey,
                     PushFormat(State->UI.FrameArena, "FPS: %.2f", 1.0f/Input->dtForFrame));
            DEBUGDisplayOffsetY += 20;
            
        }
#endif
        
#if VN_INTERNAL
        DI_EndFrame();
#endif
        
	}
}