//
// File: foldhaus_interface.cpp
// Author: Peter Slattery
// Creation Date: 2020-01-01
//
#ifndef FOLDHAUS_INTERFACE_CPP

////////////////////////////////////////
//
//     Panels
//
///////////////////////////////////////

enum panel_edit_mode
{
    PanelEdit_Modify,
    PanelEdit_Destroy,
    
    PanelEdit_Count,
};

internal void
SetPanelDefinition(panel* Panel, s32 NewPanelDefinitionIndex, app_state* State)
{
    s32 OldPanelDefinitionIndex = Panel->PanelDefinitionIndex;
    Panel->PanelDefinitionIndex = NewPanelDefinitionIndex;
    
    if(OldPanelDefinitionIndex >= 0)
    {
        GlobalPanelDefs[OldPanelDefinitionIndex].Cleanup(Panel, State);
    }
    GlobalPanelDefs[NewPanelDefinitionIndex].Init(Panel, State);
}

//
// Drag Panel Border Operation Mode

OPERATION_STATE_DEF(drag_panel_border_operation_state)
{
    panel* Panel;
    
    // NOTE(Peter): InitialPanelBounds is the bounds of the panel we are modifying,
    // it stores the value calculated when the operation mode is kicked off.
    rect2 InitialPanelBounds;
    panel_split_direction PanelEdgeDirection;
    panel_edit_mode PanelEditMode;
};

OPERATION_RENDER_PROC(UpdateAndRenderDragPanelBorder)
{
    drag_panel_border_operation_state* OpState = (drag_panel_border_operation_state*)Operation.OpStateMemory;
    rect2 PanelBounds = OpState->InitialPanelBounds;
    
    if (OpState->PanelEditMode == PanelEdit_Modify)
    {
        v4 EdgePreviewColor = v4{.3f, .3f, .3f, 1.f};
        
        v2 EdgePreviewMin = {};
        v2 EdgePreviewMax = {};
        if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
        {
            EdgePreviewMin = v2{PanelBounds.Min.x, Mouse.Pos.y};
            EdgePreviewMax = v2{PanelBounds.Max.x, Mouse.Pos.y + 1};
        }
        else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
        {
            EdgePreviewMin = v2{Mouse.Pos.x, PanelBounds.Min.y};
            EdgePreviewMax = v2{Mouse.Pos.x + 1, PanelBounds.Max.y};
        }
        
        PushRenderQuad2D(RenderBuffer, EdgePreviewMin, EdgePreviewMax, EdgePreviewColor);
    }
    else if (OpState->PanelEditMode == PanelEdit_Destroy)
    {
        rect2 PanelToDeleteBounds = {};
        if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
        {
            r32 SplitY = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y);
            if (Mouse.Pos.y > SplitY)
            {
                PanelToDeleteBounds = GetTopPanelBounds(OpState->Panel, PanelBounds);
            }
            else
            {
                PanelToDeleteBounds = GetBottomPanelBounds(OpState->Panel, PanelBounds);
            }
        }
        else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
        {
            r32 SplitX = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x);
            if (Mouse.Pos.x > SplitX)
            {
                PanelToDeleteBounds = GetRightPanelBounds(OpState->Panel, PanelBounds);
            }
            else
            {
                PanelToDeleteBounds = GetLeftPanelBounds(OpState->Panel, PanelBounds);
            }
        }
        v4 OverlayColor = v4{0, 0, 0, .3f};
        PushRenderQuad2D(RenderBuffer, PanelToDeleteBounds.Min, PanelToDeleteBounds.Max, OverlayColor);
    }
}

FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation)
{
    drag_panel_border_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_panel_border_operation_state);
    panel* Panel = OpState->Panel;
    rect2 PanelBounds = OpState->InitialPanelBounds;
    
    if (OpState->PanelEditMode == PanelEdit_Modify)
    {
        if (Panel->SplitDirection == PanelSplit_Horizontal)
        {
            r32 NewSplitY = Mouse.Pos.y;
            if (NewSplitY <= PanelBounds.Min.y)
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Top, &State->PanelSystem);
            }
            else if (NewSplitY >= PanelBounds.Max.y)
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Bottom, &State->PanelSystem);
            }
            else
            {
                Panel->SplitPercent = (NewSplitY  - PanelBounds.Min.y) / Rect2Height(PanelBounds);
            }
        }
        else if (Panel->SplitDirection == PanelSplit_Vertical)
        {
            r32 NewSplitX = Mouse.Pos.x;
            if (NewSplitX <= PanelBounds.Min.x)
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Right, &State->PanelSystem);
            }
            else if (NewSplitX >= PanelBounds.Max.x)
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Left, &State->PanelSystem);
            }
            else
            {
                Panel->SplitPercent = (NewSplitX  - PanelBounds.Min.x) / Rect2Width(PanelBounds);
            }
        }
    }
    else // PanelEdit_Destroy
    {
        if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
        {
            r32 SplitY = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y);
            if (Mouse.Pos.y > SplitY)
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Bottom, &State->PanelSystem);
            }
            else
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Top, &State->PanelSystem);
            }
        }
        else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
        {
            r32 SplitX = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x);
            if (Mouse.Pos.x > SplitX)
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Left, &State->PanelSystem);
            }
            else
            {
                ConsolidatePanelsKeepOne(Panel, Panel->Right, &State->PanelSystem);
            }
        }
    }
    
    DeactivateCurrentOperationMode(&State->Modes);
}

input_command DragPanelBorderCommands[] = {
    { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDragPanelBorderOperation },
    { KeyCode_MouseRightButton, KeyCode_Invalid, Command_Ended, EndDragPanelBorderOperation },
};

internal void
BeginDragPanelBorder(panel* Panel, panel_edit_mode PanelEditMode, rect2 PanelBounds, panel_split_direction PanelEdgeDirection, mouse_state Mouse, app_state* State)
{
    operation_mode* DragPanelBorder = ActivateOperationModeWithCommands(&State->Modes, DragPanelBorderCommands, UpdateAndRenderDragPanelBorder);
    drag_panel_border_operation_state* OpState = CreateOperationState(DragPanelBorder, &State->Modes, drag_panel_border_operation_state);
    OpState->Panel = Panel;
    OpState->InitialPanelBounds = PanelBounds;
    OpState->PanelEdgeDirection = PanelEdgeDirection;
    OpState->PanelEditMode = PanelEditMode;
}

// ----------------

//
// Drag To Split Panel Operation


OPERATION_STATE_DEF(split_panel_operation_state)
{
    panel* Panel;
    
    // NOTE(Peter): InitialPanelBounds is the bounds of the panel we are modifying,
    // it stores the value calculated when the operation mode is kicked off.
    rect2 InitialPanelBounds;
};

OPERATION_RENDER_PROC(UpdateAndRenderSplitPanel)
{
    split_panel_operation_state* OpState = (split_panel_operation_state*)Operation.OpStateMemory;
    rect2 PanelBounds = OpState->InitialPanelBounds;
    v4 EdgePreviewColor = v4{.3f, .3f, .3f, 1.f};
    
    r32 MouseDeltaX = Abs(Mouse.Pos.x - Mouse.DownPos.x);
    r32 MouseDeltaY = Abs(Mouse.Pos.y - Mouse.DownPos.y);
    
    v2 EdgePreviewMin = {};
    v2 EdgePreviewMax = {};
    if (MouseDeltaY > MouseDeltaX) // Horizontal Split
    {
        EdgePreviewMin = v2{PanelBounds.Min.x, Mouse.Pos.y};
        EdgePreviewMax = v2{PanelBounds.Max.x, Mouse.Pos.y + 1};
    }
    else // Vertical Split
    {
        EdgePreviewMin = v2{Mouse.Pos.x, PanelBounds.Min.y};
        EdgePreviewMax = v2{Mouse.Pos.x + 1, PanelBounds.Max.y};
    }
    
    PushRenderQuad2D(RenderBuffer, EdgePreviewMin, EdgePreviewMax, EdgePreviewColor);
}

FOLDHAUS_INPUT_COMMAND_PROC(EndSplitPanelOperation)
{
    split_panel_operation_state* OpState = GetCurrentOperationState(State->Modes, split_panel_operation_state);
    panel* Panel = OpState->Panel;
    rect2 PanelBounds = OpState->InitialPanelBounds;
    
    r32 XDistance = Abs(Mouse.Pos.x - Mouse.DownPos.x);
    r32 YDistance = Abs(Mouse.Pos.y - Mouse.DownPos.y);
    
    if (XDistance > YDistance)
    {
        r32 XPercent = (Mouse.Pos.x - PanelBounds.Min.x) / Rect2Width(PanelBounds);
        SplitPanelVertically(Panel, XPercent, &State->PanelSystem);
    }
    else
    {
        r32 YPercent = (Mouse.Pos.y - PanelBounds.Min.y) / Rect2Height(PanelBounds);
        SplitPanelHorizontally(Panel, YPercent, &State->PanelSystem);
    }
    
    Panel->Left->Panel.PanelDefinitionIndex = Panel->PanelDefinitionIndex;
    Panel->Left->Panel.PanelStateMemory = Panel->PanelStateMemory;
    Panel->Left->Panel.PanelStateMemorySize = Panel->PanelStateMemorySize;
    
    SetPanelDefinition(&Panel->Right->Panel, Panel->PanelDefinitionIndex, State);
    
    DeactivateCurrentOperationMode(&State->Modes);
}

input_command SplitPanelCommands[] = {
    { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndSplitPanelOperation },
};

internal void
BeginSplitPanelOperation(panel* Panel, rect2 PanelBounds, mouse_state Mouse, app_state* State)
{
    operation_mode* SplitPanel = ActivateOperationModeWithCommands(&State->Modes, SplitPanelCommands, UpdateAndRenderSplitPanel);
    split_panel_operation_state* OpState = CreateOperationState(SplitPanel, &State->Modes, split_panel_operation_state);
    OpState->Panel = Panel;
    OpState->InitialPanelBounds = PanelBounds;
}


// ----------------

#define PANEL_EDGE_CLICK_MAX_DISTANCE 6

internal b32
HandleMouseDownPanelInteractionOrRecurse(panel* Panel, panel_edit_mode PanelEditMode, rect2 PanelBounds, mouse_state Mouse, app_state* State)
{
    b32 HandledMouseInput = false;
    
    rect2 PanelSplitButtonBounds = rect2{ PanelBounds.Min, PanelBounds.Min + v2{25, 25} };
    
    if (Panel->SplitDirection == PanelSplit_NoSplit
        && PointIsInRect(PanelSplitButtonBounds, Mouse.DownPos))
    {
        BeginSplitPanelOperation(Panel, PanelBounds, Mouse, State);
        HandledMouseInput = true;
    }
    else if (Panel->SplitDirection == PanelSplit_Horizontal)
    {
        r32 SplitY = LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y);
        r32 ClickDistanceFromSplit = Abs(Mouse.DownPos.y - SplitY);
        if (ClickDistanceFromSplit < PANEL_EDGE_CLICK_MAX_DISTANCE)
        {
            BeginDragPanelBorder(Panel, PanelEditMode, PanelBounds, PanelSplit_Horizontal, Mouse, State);
            HandledMouseInput = true;
        }
        else
        {
            rect2 TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
            rect2 BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
            if (PointIsInRect(BottomPanelBounds, Mouse.DownPos))
            {
                HandleMouseDownPanelInteractionOrRecurse(&Panel->Bottom->Panel, PanelEditMode, BottomPanelBounds, Mouse, State);
            }
            if (PointIsInRect(TopPanelBounds, Mouse.DownPos))
            {
                HandleMouseDownPanelInteractionOrRecurse(&Panel->Top->Panel, PanelEditMode, TopPanelBounds, Mouse, State);
            }
        }
    }
    else if (Panel->SplitDirection == PanelSplit_Vertical)
    {
        r32 SplitX = LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x);
        r32 ClickDistanceFromSplit = Abs(Mouse.DownPos.x - SplitX);
        if (ClickDistanceFromSplit < PANEL_EDGE_CLICK_MAX_DISTANCE)
        {
            BeginDragPanelBorder(Panel, PanelEditMode, PanelBounds, PanelSplit_Vertical, Mouse, State);
            HandledMouseInput = true;
        }
        else
        {
            rect2 LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
            rect2 RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
            if (PointIsInRect(LeftPanelBounds, Mouse.DownPos))
            {
                HandleMouseDownPanelInteractionOrRecurse(&Panel->Left->Panel, PanelEditMode, LeftPanelBounds, Mouse, State);
            }
            if (PointIsInRect(RightPanelBounds, Mouse.DownPos))
            {
                HandleMouseDownPanelInteractionOrRecurse(&Panel->Right->Panel, PanelEditMode, RightPanelBounds, Mouse, State);
            }
        }
    }
    
    return HandledMouseInput;
}

internal b32
HandleMousePanelInteraction(panel_system* PanelSystem, rect2 WindowBounds, mouse_state Mouse, app_state* State)
{
    b32 HandledMouseInput = false;
    
    panel* FirstPanel = &PanelSystem->Panels[0].Panel;
    if (MouseButtonTransitionedDown(Mouse.LeftButtonState))
    {
        HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, PanelEdit_Modify, WindowBounds, Mouse, State);
    }
    else if (MouseButtonTransitionedDown(Mouse.RightButtonState))
    {
        HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, PanelEdit_Destroy, WindowBounds, Mouse, State);
    }
    
    return HandledMouseInput;
}

internal void
DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, v4 Color, mouse_state* Mouse, render_command_buffer* RenderBuffer)
{
    r32 MouseLeftEdgeDistance = Abs(Mouse->Pos.x - PanelMin.x);
    r32 MouseRightEdgeDistance = Abs(Mouse->Pos.x - PanelMax.x);
    r32 MouseTopEdgeDistance = Abs(Mouse->Pos.y - PanelMax.y);
    r32 MouseBottomEdgeDistance = Abs(Mouse->Pos.y - PanelMin.y);
    
    PushRenderBoundingBox2D(RenderBuffer, PanelMin, PanelMax, 1, Color);
    v4 HighlightColor = v4{.3f, .3f, .3f, 1.f};
    r32 HighlightThickness = 1;
    if (MouseLeftEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
    {
        v2 LeftEdgeMin = PanelMin;
        v2 LeftEdgeMax = v2{PanelMin.x + HighlightThickness, PanelMax.y};
        PushRenderQuad2D(RenderBuffer, LeftEdgeMin, LeftEdgeMax, HighlightColor);
        Mouse->CursorType = CursorType_HorizontalArrows;
    }
    else if (MouseRightEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
    {
        v2 RightEdgeMin = v2{PanelMax.x - HighlightThickness, PanelMin.y};
        v2 RightEdgeMax = PanelMax;
        PushRenderQuad2D(RenderBuffer, RightEdgeMin, RightEdgeMax, HighlightColor);
        Mouse->CursorType = CursorType_HorizontalArrows;
    }
    else if (MouseTopEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
    {
        v2 TopEdgeMin = v2{PanelMin.x, PanelMax.y - HighlightThickness};
        v2 TopEdgeMax = PanelMax;
        PushRenderQuad2D(RenderBuffer, TopEdgeMin, TopEdgeMax, HighlightColor);
        Mouse->CursorType = CursorType_VerticalArrows;
    }
    else if (MouseBottomEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
    {
        v2 BottomEdgeMin = PanelMin;
        v2 BottomEdgeMax = v2{PanelMax.x, PanelMin.y + HighlightThickness};
        PushRenderQuad2D(RenderBuffer, BottomEdgeMin, BottomEdgeMax, HighlightColor);
        Mouse->CursorType = CursorType_VerticalArrows;
    }
}

internal void
DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, rect2 FooterBounds, mouse_state Mouse, app_state* State)
{
    PushRenderQuad2D(RenderBuffer, FooterBounds.Min, v2{FooterBounds.Max.x, FooterBounds.Min.y + 25}, v4{.5f, .5f, .5f, 1.f});
    PushRenderQuad2D(RenderBuffer, FooterBounds.Min, FooterBounds.Min + v2{25, 25}, WhiteV4);
    
    rect2 PanelSelectBtnBounds = MakeRect2MinDim(FooterBounds.Min + v2{30, 1}, v2{100, 23});
    
    if (Panel->PanelSelectionMenuOpen)
    {
        rect2 ButtonBounds = MakeRect2MinDim(v2{ PanelSelectBtnBounds.Min.x, FooterBounds.Max.y }, v2{ 100, 25 });
        
        rect2 MenuBounds = rect2
        {
            ButtonBounds.Min,
            v2{
                ButtonBounds.Min.x + Rect2Width(ButtonBounds), ButtonBounds.Min.y + (Rect2Height(ButtonBounds) * GlobalPanelDefsCount)
            },
        };
        
        if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
            && !PointIsInRect(MenuBounds, Mouse.DownPos))
        {
            Panel->PanelSelectionMenuOpen = false;
        }
        
        for (s32 i = 0; i < GlobalPanelDefsCount; i++)
        {
            panel_definition Def = GlobalPanelDefs[i];
            gs_string DefName = MakeString(Def.PanelName, Def.PanelNameLength);
            if (ui_Button(&State->Interface, DefName, ButtonBounds))
            {
                SetPanelDefinition(Panel, i, State);
                Panel->PanelSelectionMenuOpen = false;
            }
            
            ButtonBounds = Rect2TranslateY(ButtonBounds, Rect2Height(ButtonBounds));
        }
    }
    
    if (ui_Button(&State->Interface, MakeString("Select"), PanelSelectBtnBounds))
    {
        Panel->PanelSelectionMenuOpen = !Panel->PanelSelectionMenuOpen;
    }
    
}

internal void
RenderPanel(panel* Panel, rect2 PanelBounds, rect2 WindowBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse)
{
    Assert(Panel->PanelDefinitionIndex >= 0);
    
    rect2 FooterBounds = rect2{
        PanelBounds.Min,
        v2{PanelBounds.Max.x, PanelBounds.Min.y + 25},
    };
    rect2 PanelViewBounds = rect2{
        v2{PanelBounds.Min.x, FooterBounds.Max.y},
        PanelBounds.Max,
    };
    
    panel_definition Definition = GlobalPanelDefs[Panel->PanelDefinitionIndex];
    Definition.Render(*Panel, PanelViewBounds, RenderBuffer, State, Context);
    
    PushRenderOrthographic(RenderBuffer, WindowBounds);
    DrawPanelFooter(Panel, RenderBuffer, FooterBounds, Mouse, State);
}

internal void
DrawAllPanels(panel_layout PanelLayout, render_command_buffer* RenderBuffer, mouse_state* Mouse, app_state* State, context Context)
{
    for (u32 i = 0; i < PanelLayout.PanelsCount; i++)
    {
        panel_with_layout PanelWithLayout = PanelLayout.Panels[i];
        panel* Panel = PanelWithLayout.Panel;
        rect2 PanelBounds = PanelWithLayout.Bounds;
        
        RenderPanel(Panel, PanelBounds, State->WindowBounds, RenderBuffer, State, Context, *Mouse);
        v4 BorderColor = v4{0, 0, 0, 1};
        
        PushRenderOrthographic(RenderBuffer, State->WindowBounds);
        DrawPanelBorder(*Panel, PanelBounds.Min, PanelBounds.Max, BorderColor, Mouse, RenderBuffer);
    }
}

#define FOLDHAUS_INTERFACE_CPP
#endif // FOLDHAUS_INTERFACE_CPP