/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 19.08.2015
 *
 * Panel layout and general view functions for 4coder
 *
 */

// TOP

struct Panel_Divider{
    Panel_Divider *next;
    i32 parent;
    i32 which_child;
    i32 child1, child2;
    b32 v_divider;
    i32 pos;
};

struct Screen_Region{
    i32_Rect full;
    i32_Rect inner;
    i32_Rect prev_inner;
    i32 l_margin, r_margin;
    i32 t_margin, b_margin;
};

struct Panel{
    Panel *next;
    Panel *prev;

    struct View *view;
    i32 parent;
    i32 which_child;
    
    int ALLOCED;
    
    union{
        struct{
            i32_Rect full;
            i32_Rect inner;
            i32_Rect prev_inner;
            i32 l_margin, r_margin;
            i32 t_margin, b_margin;
        };
        Screen_Region screen_region;
    };
};

struct Editing_Layout{
    Panel *panels;
    Panel free_sentinel;
    Panel used_sentinel;
    Panel_Divider *dividers;
    Panel_Divider *free_divider;
    i32 panel_count, panel_max_count;
    i32 root;
    i32 active_panel;
    i32 full_width, full_height;
};

struct Divider_And_ID{
    Panel_Divider* divider;
    i32 id;
};

struct Panel_And_ID{
    Panel* panel;
    i32 id;
};

internal void
panel_init(Panel *panel){
    panel->view = 0;
    panel->parent = -1;
    panel->which_child = 0;
    panel->screen_region.full = {};
    panel->screen_region.inner = {};
    panel->screen_region.prev_inner = {};
    panel->l_margin = 3;
    panel->r_margin = 3;
    panel->t_margin = 3;
    panel->b_margin = 3;
}

internal Divider_And_ID
layout_alloc_divider(Editing_Layout *layout){
    Divider_And_ID result;
    
    Assert(layout->free_divider);
    result.divider = layout->free_divider;
    layout->free_divider = result.divider->next;
    
    *result.divider = {};
    result.divider->parent = -1;
    result.divider->child1 = -1;
    result.divider->child2 = -1;
    result.id = (i32)(result.divider - layout->dividers);
    if (layout->panel_count == 1){
        layout->root = result.id;
    }
    
    return(result);
}

internal Divider_And_ID
layout_get_divider(Editing_Layout *layout, i32 id){
    Divider_And_ID result;
    
    Assert(id >= 0 && id < layout->panel_max_count-1);
    result.id = id;
    result.divider = layout->dividers + id;
    
    return(result);
}

internal void
layout_free_divider(Editing_Layout *layout, Panel_Divider *divider){
    divider->next = layout->free_divider;
    layout->free_divider = divider;
}

internal Panel_And_ID
layout_alloc_panel(Editing_Layout *layout){
    Panel_And_ID result = {};
    
    Assert(layout->panel_count < layout->panel_max_count);
    ++layout->panel_count;
    
    result.panel = layout->free_sentinel.next;
    dll_remove(result.panel);
    dll_insert(&layout->used_sentinel, result.panel);
    
    panel_init(result.panel);
    
    result.id = (i32)(result.panel - layout->panels);
    
    result.panel->ALLOCED = 1;
    
    return(result);
}

internal void
layout_free_panel(Editing_Layout *layout, Panel *panel){
    dll_remove(panel);
    dll_insert(&layout->free_sentinel, panel);
    --layout->panel_count;
    
    panel->ALLOCED = 0;
}

internal Divider_And_ID
layout_calc_divider_id(Editing_Layout *layout, Panel_Divider *divider){
    Divider_And_ID result;
    result.divider = divider;
    result.id = (i32)(divider - layout->dividers);
    return result;
}

struct Split_Result{
    Panel_Divider *divider;
    Panel *panel;
};

internal Split_Result
layout_split_panel(Editing_Layout *layout, Panel *panel, b32 vertical){
    Split_Result result = {};
    Divider_And_ID div = {}, parent_div = {};
    Panel_And_ID new_panel = {};
    
    div = layout_alloc_divider(layout);
    if (panel->parent != -1){
        parent_div = layout_get_divider(layout, panel->parent);
        if (panel->which_child == -1){
            parent_div.divider->child1 = div.id;
        }
        else{
            parent_div.divider->child2 = div.id;
        }
    }
    
    div.divider->parent = panel->parent;
    div.divider->which_child = panel->which_child;
    if (vertical){
        div.divider->v_divider = 1;
        div.divider->pos = (panel->full.x0 + panel->full.x1) / 2;
    }
    else{
        div.divider->v_divider = 0;
        div.divider->pos = (panel->full.y0 + panel->full.y1) / 2;
    }

    new_panel = layout_alloc_panel(layout);
    panel->parent = div.id;
    panel->which_child = -1;
    new_panel.panel->parent = div.id;
    new_panel.panel->which_child = 1;

    result.divider = div.divider;
    result.panel = new_panel.panel;
    
    return result;
}

internal void
panel_fix_internal_area(Panel *panel){
    panel->inner.x0 = panel->full.x0 + panel->l_margin;
    panel->inner.x1 = panel->full.x1 - panel->r_margin;
    panel->inner.y0 = panel->full.y0 + panel->t_margin;
    panel->inner.y1 = panel->full.y1 - panel->b_margin;
}

internal void
layout_fix_all_panels(Editing_Layout *layout){
    Panel *panel;
    Panel_Divider *dividers = layout->dividers;
    i32 panel_count = layout->panel_count;
    i32_Rect r;
    i32 pos, which_child, action;
    Divider_And_ID div;
    
    if (panel_count > 1){
        for (panel = layout->used_sentinel.next;
            panel != &layout->used_sentinel;
            panel = panel->next){
            
            r.x0 = 0;
            r.x1 = r.x0 + layout->full_width;
            r.y0 = 0;
            r.y1 = r.y0 + layout->full_height;
            
            which_child = panel->which_child;
            
            div.id = panel->parent;
            
            for (;;){
                Assert(div.id != -1);
                div.divider = dividers + div.id;
                pos = div.divider->pos;
                
                action = (div.divider->v_divider << 1) | (which_child > 0);
                switch (action){
                case 0: // v_divider : 0, which_child : -1
                    if (pos < r.y1) r.y1 = pos;
                    break;
                case 1: // v_divider : 0, which_child : 1
                    if (pos > r.y0) r.y0 = pos;
                    break;
                case 2: // v_divider : 1, which_child : -1
                    if (pos < r.x1) r.x1 = pos;
                    break;
                case 3: // v_divider : 1, which_child : 1
                    if (pos > r.x0) r.x0 = pos;
                    break;
                }
                
                if (div.id != layout->root){
                    div.id = div.divider->parent;
                    which_child = div.divider->which_child;
                }
                else{
                    break;
                }
            }
            
            panel->full = r;
            panel_fix_internal_area(panel);
        }
    }

    else{
        panel = layout->used_sentinel.next;
        panel->full.x0 = 0;
        panel->full.y0 = 0;
        panel->full.x1 = layout->full_width;
        panel->full.y1 = layout->full_height;
        panel_fix_internal_area(panel);
    }
}

internal void
layout_refit(Editing_Layout *layout, i32 prev_width, i32 prev_height){
    
    Panel_Divider *dividers = layout->dividers;
    i32 max = layout->panel_max_count - 1;
    
    f32 h_ratio, v_ratio;

    Panel_Divider *divider = dividers;
    i32 i;

    if (layout->panel_count > 1){
        Assert(prev_width != 0 && prev_height != 0);

        h_ratio = ((f32)layout->full_width) / prev_width;
        v_ratio = ((f32)layout->full_height) / prev_height;

        for (i = 0; i < max; ++i, ++divider){
            if (divider->v_divider){
                divider->pos = ROUND32((divider->pos) * h_ratio);
            }
            else{
                divider->pos = ROUND32((divider->pos) * v_ratio);
            }
        }
    }

    layout_fix_all_panels(layout);
}

// BOTTOM

