//
// File: foldhaus_panel_profiler.h
// Author: Peter Slattery
// Creation Date: 2020-01-01
//
#ifndef FOLDHAUS_PANEL_PROFILER_H

input_command ProfilerView_Commands[] = {{}};
s32 ProfilerView_CommandsCount = 0;

GSMetaTag(panel_init);
GSMetaTag(panel_type_profiler);
internal void
ProfilerView_Init(panel* Panel, app_state* State, context Context)
{
    
}

GSMetaTag(panel_cleanup);
GSMetaTag(panel_type_profiler);
internal void
ProfilerView_Cleanup(panel* Panel, app_state* State)
{
    
}

internal void
RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Transient)
{
    rect2 Bounds = ui_LayoutRemaining(*Layout);
    r32 Width = Rect2Width(Bounds);
    r32 DepthHeight = 32;
    
    s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
    r32 FrameTotalCycles = (r32)(VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles);
    
    r32 NextThreadTop = Bounds.Max.y;
    
    for (s32 t = 0; t < VisibleFrame->ThreadCount; t++)
    {
        debug_scope_record_list ThreadCalls = VisibleFrame->ThreadCalls[t];
        
        gs_string String = PushString(Transient, 256);
        
        r32 ThreadScopeMin = Bounds.Max.y;
        
        //PrintF(&String, "Thread %d", ThreadCalls.ThreadId);
        //ui_Label(Interface, String, rect2{ThreadScopeMin);
        
        r32 Hue = (r32)(t) / (r32)(VisibleFrame->ThreadCount);
        Hue += (.5f * (t % 2));
        v4 ThreadHSV = v4{ 360.0f * Hue, .5f, 1.0f, 1.0f };
        v4 ThreadRGB = HSVToRGB(ThreadHSV);
        
        for (s32 i = 0; i < ThreadCalls.Count; i++)
        {
            scope_record* Record = ThreadCalls.Calls + i;
            scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash);
            s64 OffsetStart = Record->StartCycles - FrameStartCycles;
            s64 OffsetEnd = Record->EndCycles - FrameStartCycles;
            r32 PercentStart = (r32)(OffsetStart) / FrameTotalCycles;
            r32 PercentEnd = (r32)(OffsetEnd) / FrameTotalCycles;
            r32 PercentWidth = PercentEnd - PercentStart;
            
            rect2 ScopeBounds = {
                v2{0, 0},
                v2{PercentWidth * Width, DepthHeight - 4},
            };
            v2 Offset = {
                Bounds.Min.x + (PercentStart * Width),
                NextThreadTop - ((Record->CallDepth + 1) * DepthHeight)
            };
            ScopeBounds = Rect2Translate(ScopeBounds, Offset);
            ThreadScopeMin = Min(ScopeBounds.Min.y, ThreadScopeMin);
            
            if (Rect2Width(ScopeBounds) >= 1)
            {
                v4 Color = ThreadRGB;
                if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos))
                {
                    Color = GreenV4;
                    
                    ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover"));
                    {
                        s64 Cycles = (Record->EndCycles - Record->StartCycles);
                        r32 PercentFrame = (r32)(Cycles) / FrameTotalCycles;
                        PrintF(&String, "%S : %.2f%% frame | %dcy",
                               Name->Name,
                               PercentFrame,
                               Cycles);
                        ui_Label(Interface, String);
                    }
                    ui_EndMousePopup(Interface);
                }
                
                ui_FillRect(Interface, ScopeBounds, Color);
                ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4);
            }
        }
        
        NextThreadTop = ThreadScopeMin;
    }
}

internal void
RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory)
{
    char Backbuffer[256];
    gs_string String = MakeString(Backbuffer, 0, 256);
    
    ui_column_spec ColumnWidths[] = {
        { UIColumnSize_Fixed, 256 },
        { UIColumnSize_Fixed, 128 },
        { UIColumnSize_Fixed, 128 },
        { UIColumnSize_Fixed, 128 },
        { UIColumnSize_Fixed, 128 }};
    ui_BeginRow(Interface, 5, &ColumnWidths[0]);
    {
        ui_Label(Interface, MakeString("Procedure"));
        ui_Label(Interface, MakeString("% Frame"));
        ui_Label(Interface, MakeString("Seconds"));
        ui_Label(Interface, MakeString("Cycles"));
        ui_Label(Interface, MakeString("Calls"));
    }
    ui_EndRow(Interface);
    
    s32 CountedScopes = 0;
    for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
    {
        scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
        if (NameEntry.Hash != 0)
        {
            CountedScopes += 1;
        }
    }
    
    ui_BeginList(Interface, MakeString("Scope List"), 10, CountedScopes);
    ui_BeginRow(Interface, 5, &ColumnWidths[0]);
    for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
    {
        scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
        if (NameEntry.Hash != 0)
        {
            collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
            
            PrintF(&String, "%S", NameEntry.Name);
            ui_Label(Interface, String);
            
            PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime);
            ui_Label(Interface, String);
            
            PrintF(&String, "%fs", CollatedRecord->TotalSeconds);
            ui_Label(Interface, String);
            
            PrintF(&String, "%dcy", CollatedRecord->TotalCycles);
            ui_Label(Interface, String);
            
            PrintF(&String, "%d", CollatedRecord->CallCount);
            ui_Label(Interface, String);
        }
    }
    ui_EndRow(Interface);
    ui_EndList(Interface);
}

GSMetaTag(panel_render);
GSMetaTag(panel_type_profiler);
internal void
ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
    gs_memory_arena* Memory = State->Transient;
    gs_string String = PushString(Memory, 256);
    
    v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 };
    
    r32 FrameListHeight = 64;
    rect2 FrameListBounds, ProcListBounds;
    RectHSplitAtDistanceFromTop(PanelBounds, FrameListHeight, &FrameListBounds, &ProcListBounds);
    rect2 FrameListInner = RectInset(FrameListBounds, 4);
    
    r32 SingleFrameStep = Rect2Width(FrameListInner) / DEBUG_FRAME_COUNT;
    r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2);
    
    ui_OutlineRect(&State->Interface, FrameListBounds, 2, WhiteV4);
    if (MouseButtonHeldDown(Context.Mouse.LeftButtonState))
    {
        if (PointIsInRect(FrameListBounds, Context.Mouse.Pos))
        {
            v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos);
            s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep);
            if (ClosestFrameIndex >= 0 && ClosestFrameIndex < DEBUG_FRAME_COUNT)
            {
                GlobalDebugServices->RecordFrames = false;
                GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex;
            }
        }
    }
    
    rect2 FrameBounds = MakeRect2MinDim(FrameListInner.Min, v2{SingleFrameWidth, Rect2Height(FrameListInner)});
    for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++)
    {
        rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep);
        s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F);
        if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; }
        v4 Color = FrameColors[Clamp(0, FramesAgo, 3)];
        ui_FillRect(&State->Interface, PositionedFrameBounds, Color);
    }
    
    debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
    
    ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout"));
    
    ui_BeginRow(&State->Interface, 4);
    {
        s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
        s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
        u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1;
        PrintF(&String, "Frame %d", CurrentDebugFrame);
        ui_Label(&State->Interface, String);
        
        PrintF(&String, "Total Cycles: %lld", FrameTotalCycles);
        ui_Label(&State->Interface, String);
        
        // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could
        // be removed, or used for something else
        ui_ReserveBounds(&State->Interface, Layout, true);
        
        if (ui_Button(&State->Interface, MakeString("Resume Recording")))
        {
            GlobalDebugServices->RecordFrames = true;
        }
    }
    ui_EndRow(&State->Interface);
    
    ui_BeginRow(&State->Interface, 8);
    {
        if (ui_Button(&State->Interface, MakeString("Scope View")))
        {
            GlobalDebugServices->Interface.FrameView = FRAME_VIEW_PROFILER;
        }
        if (ui_Button(&State->Interface, MakeString("List View")))
        {
            GlobalDebugServices->Interface.FrameView = FRAME_VIEW_SCOPE_LIST;
        }
    }
    ui_EndRow(&State->Interface);
    
    if (GlobalDebugServices->Interface.FrameView == FRAME_VIEW_PROFILER)
    {
        RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory);
    }
    else
    {
        RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory);
    }
    
    ui_PopLayout(&State->Interface, MakeString("Profiler Layout"));
}


#define FOLDHAUS_PANEL_PROFILER_H
#endif // FOLDHAUS_PANEL_PROFILER_H
