#import "modules/std.sx";
#import "modules/ui/types.sx";

RenderNodeType :: enum {
    rect;
    rounded_rect;
    text;
    image;
    clip_push;
    clip_pop;
    opacity_push;
    opacity_pop;
}

RenderNode :: struct {
    type: RenderNodeType;
    frame: Frame;

    // Rect / rounded_rect
    fill_color: Color;
    stroke_color: Color;
    stroke_width: f32;
    corner_radius: f32;

    // Text
    text: string;
    font_size: f32;
    text_color: Color;

    // Image
    texture_id: u32;
    uv_min: Point;
    uv_max: Point;

    // Opacity
    opacity: f32;

    depth: s64;
}

RenderTree :: struct {
    nodes: List(RenderNode);
    generation: s64;

    init :: () -> RenderTree {
        RenderTree.{ generation = 0 };
    }

    clear :: (self: *RenderTree) {
        self.nodes.len = 0;
        self.generation += 1;
    }

    add :: (self: *RenderTree, node: RenderNode) -> s64 {
        idx := self.nodes.len;
        self.nodes.append(node);
        idx;
    }
}

// Stateful builder — views use this to emit render nodes

RenderContext :: struct {
    tree: *RenderTree;
    clip_depth: s64;
    opacity: f32;
    depth: s64;

    init :: (tree: *RenderTree) -> RenderContext {
        RenderContext.{
            tree = tree,
            clip_depth = 0,
            opacity = 1.0,
            depth = 0
        };
    }

    add_rect :: (self: *RenderContext, frame: Frame, fill: Color) {
        self.tree.add(.{
            type = .rect,
            frame = frame,
            fill_color = fill,
            opacity = self.opacity,
            depth = self.depth,
        });
        self.depth += 1;
    }

    add_rounded_rect :: (self: *RenderContext, frame: Frame, fill: Color, radius: f32) {
        self.tree.add(.{
            type = .rounded_rect,
            frame = frame,
            fill_color = fill,
            corner_radius = radius,
            opacity = self.opacity,
            depth = self.depth,
        });
        self.depth += 1;
    }

    add_stroked_rect :: (self: *RenderContext, frame: Frame, fill: Color, stroke: Color, stroke_w: f32, radius: f32) {
        self.tree.add(.{
            type = .rounded_rect,
            frame = frame,
            fill_color = fill,
            stroke_color = stroke,
            stroke_width = stroke_w,
            corner_radius = radius,
            opacity = self.opacity,
            depth = self.depth,
        });
        self.depth += 1;
    }

    add_text :: (self: *RenderContext, frame: Frame, text: string, font_size: f32, color: Color) {
        self.tree.add(.{
            type = .text,
            frame = frame,
            text = text,
            font_size = font_size,
            text_color = color,
            opacity = self.opacity,
            depth = self.depth,
        });
        self.depth += 1;
    }

    add_image :: (self: *RenderContext, frame: Frame, texture_id: u32) {
        self.add_image_uv(frame, texture_id, Point.zero(), Point.{ x = 1.0, y = 1.0 });
    }

    add_image_uv :: (self: *RenderContext, frame: Frame, texture_id: u32, uv_min: Point, uv_max: Point) {
        self.tree.add(.{
            type = .image,
            frame = frame,
            texture_id = texture_id,
            uv_min = uv_min,
            uv_max = uv_max,
            opacity = self.opacity,
            depth = self.depth,
        });
        self.depth += 1;
    }

    push_clip :: (self: *RenderContext, frame: Frame) {
        self.tree.add(.{
            type = .clip_push,
            frame = frame,
            depth = self.depth,
        });
        self.clip_depth += 1;
    }

    pop_clip :: (self: *RenderContext) {
        self.tree.add(.{
            type = .clip_pop,
            depth = self.depth,
        });
        self.clip_depth -= 1;
    }

    push_opacity :: (self: *RenderContext, alpha: f32) {
        prev := self.opacity;
        self.opacity = prev * alpha;
        self.tree.add(.{
            type = .opacity_push,
            opacity = self.opacity,
            depth = self.depth,
        });
    }

    pop_opacity :: (self: *RenderContext, prev_opacity: f32) {
        self.tree.add(.{
            type = .opacity_pop,
            depth = self.depth,
        });
        self.opacity = prev_opacity;
    }
}
