#ifndef prototype1_src_l2_gui_widget_h
#define prototype1_src_l2_gui_widget_h

#include "../../../gen/l1/geom.h"
#include "../../l1/core/util.h"

#define WIDGET_DIM_INF 1000000

bool is_widget_size_limit_contain_inf(uvec2 max_limits){
    return max_limits.x >= WIDGET_DIM_INF || max_limits.y >= WIDGET_DIM_INF;
}

void assert_sane_widget_size(uvec2 sz){
    assert(sz.x < WIDGET_DIM_INF || sz.y < WIDGET_DIM_INF);
}

void assert_sane_widget_size_limits(uvec2 max_limits){
    assert(max_limits.x <= WIDGET_DIM_INF || max_limits.y <= WIDGET_DIM_INF);
}

typedef struct{
    ivec2 lt, rb;
} BorderS32;

bool BorderS32_empty(BorderS32 self){
    return self.lt.x >= self.rb.x || self.lt.y >= self.rb.y;
}

BorderS32 BorderS32_intersect(BorderS32 a, BorderS32 b){
    return (BorderS32){
        {MAX_S32(a.lt.x, b.lt.x), MAX_S32(a.lt.y, b.lt.y)},
        {MAX_S32(a.rb.x, b.rb.x), MAX_S32(a.rb.y, b.rb.y)},
    };
}

uvec2 widget_size_max_of_all(uvec2 a, uvec2 b){
    return (uvec2){MAX_U32(a.x, b.x), MAX_U32(a.y, b.y)};
}

uvec2 widget_size_min_of_all(uvec2 a, uvec2 b){
    return (uvec2){MIN_U32(a.x, b.x), MIN_U32(a.y, b.y)};
}

typedef struct {
    /* .x == WIDGET_DIM_INF indicates that no preparation was made before the draw operation.
     * Calling draw at that state will cause abort. draw operation will reset this flag. This makes framework foolproof.
     * Initialize and reset with {WIDGET_DIM_INF, 0} */
    uvec2 sz_my_choice;
} Widget;

Widget Widget_new(){
    return (Widget){.sz_my_choice = {WIDGET_DIM_INF, 0}};
}

#include "../../../gen/l1_5/eve/gui/Widget.h"

uvec2 MutRefWidget_draw_prepare(MutRefWidget self, uvec2 max_limits){
    if (!self.r)
        return (uvec2){0};
    self.r->sz_my_choice = MutRefWidget_DRAW_PREPARE(self, max_limits);
    assert_sane_widget_size(self.r->sz_my_choice);
    return self.r->sz_my_choice;
}

void MutRefWidget_draw(MutRefWidget self, ivec2 drawing_offset, uvec2 surface_sz, BorderS32 border){
    if (!self.r)
        return;
    if (self.r->sz_my_choice.x >= WIDGET_DIM_INF)
        abortf("Drawing widget before negotiating it's size\n");
    MutRefWidget_draw(self, drawing_offset, surface_sz, border);
    self.r->sz_my_choice = (uvec2){WIDGET_DIM_INF, 0};
}

/* Some very simple widgets */

typedef struct {
    Widget base;
    U32 width;
    U32 height;
} EmptyWidget;

uvec2 Widget_Table_EmptyWidget_DRAW_PREPARE(Widget* ug, uvec2 limits){
    EmptyWidget* self = (EmptyWidget*)ug;
    uvec2 R = widget_size_min_of_all((uvec2){self->width, self->height}, limits);
    return is_widget_size_limit_contain_inf(R) ? (uvec2){0, 0} : R;
}

void Widget_Table_EmptyWidget_DRAW(Widget* ug, ivec2 drawing_offset, uvec2 surface_sz, BorderS32 border){}

void Widget_Table_EmptyWidget_drop(Widget* ug){}

const Widget_Table Widget_Table_EmptyWidget = {
    .DRAW_PREPARE = Widget_Table_EmptyWidget_DRAW_PREPARE,
    .DRAW = Widget_Table_EmptyWidget_DRAW,
    .drop = Widget_Table_EmptyWidget_drop,
};

BoxWidget EmptyWidget_new_box(U32 width, U32 height){
    EmptyWidget* r = safe_malloc(sizeof(EmptyWidget));
    *r = (EmptyWidget){.base = Widget_new(), .width = width, .height = height};
    return (BoxWidget){.r = (Widget*)r, .t = &Widget_Table_EmptyWidget};
}

#endif