#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 "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_scene.h"
#include "vn_scene_view.h"
#include "vn_workspace.h"
#include "vn_animation_curve.h"
#include "vn_theme_dark.h"
#include "vn_character.h"

#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_scene.cpp"
#include "vn_scene_view.cpp"
#include "vn_workspace.cpp"
#include "vn_character.cpp"

struct vn_state
{
    memory_arena *Arena;
    memory_arena *FrameArena;
    
    glyph_atlas *GlyphAtlas;
    
    config *Config;
    
    ui UI;
    workspace Workspace;
    animation_curve_state AnimationCurveState;
    
    character_registry CharacterRegistry;
    render_handle BackgroundTexture;
    
    scene_view SceneView;
    
#if VN_INTERNAL
    debug_settings DebugSettings;
#endif
};

static render_handle CreateTextureFromPath(vn_render_commands *Commands, string Path)
{
    render_handle Result = {};
    
    temporary_memory Scratch = GetScratch();
    platform_file_handle File = Platform.OpenFile(Path, PlatformAccess_Read);
    if(File.IsValid)
    {
        u64 DataCount = Platform.GetFileSize(File);
        u8 *Data = PushArray(Scratch.Arena, u8, DataCount);
        Platform.ReadFile(File, Data, 0, DataCount);
        
        s32 Width, Height, BPP;
        u8 *TextureData = stbi_load_from_memory(Data, DataCount, &Width, &Height, &BPP, 0);
        
        render_texture_format TextureFormat = Render_TextureFormat_Invalid;
        switch(BPP)
        {
            InvalidDefaultCase;
            case 1: { TextureFormat = Render_TextureFormat_R8; } break;
            case 3: { TextureFormat = Render_TextureFormat_RGB8; } break;
            case 4: { TextureFormat = Render_TextureFormat_RGBA8; } break;
        }
        
        Result = Commands->AllocateTexture(V2S32(Width, Height), TextureFormat, TextureData);
        
        stbi_image_free(TextureData);
        
        Platform.CloseFile(File);
    }
    ReleaseScratch(Scratch);
    return(Result);
}


VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
{
    SetThreadContext(ThreadContext);
    Platform = Memory->PlatformAPI;
    
    vn_state *State = Memory->State;
    
    //- sixten: initialize application state
    if(!Memory->State)
    {
        memory_arena *Arena = ArenaAllocate(Gigabytes(1));
        State = Memory->State = PushStruct(Arena, vn_state);
        State->Arena = Arena;
        State->FrameArena = ArenaAllocate(Gigabytes(1));
        
        State->GlyphAtlas = CreateGlyphAtlas(RenderCommands);
        State->Config = CreateConfig();
        
        //- sixten: load assets
        State->BackgroundTexture = CreateTextureFromPath(RenderCommands, StrLit("data/backgrounds/test.png"));
        
        //- sixten: setup config binds and load current config
        {
            Config_BindS32(State->Config, StrLit("Platform/RefreshRate"),   &Input->RefreshRate, 60);
#if VN_INTERNAL
            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"));
        }
        
        //- sixten: load startup scene
        scene_view *SceneView = &State->SceneView;
        SV_Init(SceneView, State->Arena);
        SceneView->BackgroundTexture = State->BackgroundTexture;
        
        //- sixten: create mock characters
        CR_Init(&State->CharacterRegistry);
        character_entry *Arthur = CR_EntryFromName(StrLit("Arthur"));
        UnusedVariable(Arthur);
        
        CR_EntryFromName(StrLit("Catherine"));
        CR_EntryFromName(StrLit("Quinn"));
        CR_EntryFromName(StrLit("Margaret"));
        
        UI_Init(&State->UI);
        W_Init(&State->Workspace);
        AC_Init(&State->AnimationCurveState);
    }
    
#if VN_INTERNAL
    DEBUG_DebugSettings = &State->DebugSettings;
#endif
    
    //- sixten: begin new frame
    ArenaClear(State->FrameArena);
    AC_NewFrame(&State->AnimationCurveState, Input->dtForFrame);
    UI_NewFrame(&State->UI, Input->EventList, Input->MouseP, State->GlyphAtlas);
    SV_SetState(&State->SceneView);
    
    //- sixten: build the ui
    UI_BeginBuild(RenderCommands->RenderDim);
    {
        b32 EditorMode = true;
        if(EditorMode)
        {
            W_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas);
        }
        else
        {
            SV_BuildSceneView(Input);
        }
    }
    UI_EndBuild();
    
    
    //- sixten: update the scene
    SV_Update(State->FrameArena, Input);
    
    //- 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, V3(0.1, 0.1, 0.1));
        
        UI_RenderFrame(&Group, State->GlyphAtlas);
        
        if(DEBUG_DebugSettings->ListHotAndActive)
        {
            PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 20), 15, Color_Grey,
                     PushFormat(State->UI.FrameArena, "Hot: %S:%llu", UI_GetBoxNameByKey(UI_GetHot()), UI_GetHot()));
            PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 40), 15, Color_Grey,
                     PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_GetBoxNameByKey(UI_GetActive()), UI_GetActive()));
        }
    }
}