const std = @import("std");

const Canvas = @import("canvas.zig").Canvas;
const value = @import("value.zig");
const Value = value.Value;
const Vm = @import("vm.zig");

fn notAScribble(vm: *Vm, val: Value) Vm.Error {
    return vm.throw(
        "the brush returned a {s}, which cannot be drawn. return a scribble (e.g. fill, stroke, list) instead",
        .{val.typeName()},
    );
}

fn renderRec(vm: *Vm, canvas: *Canvas, val: Value, depth: usize, max_depth: usize) !void {
    if (depth > max_depth) {
        return vm.throw(
            "the brush returned a scribble that's nested too deep ({} levels). try generating lists that aren't as deep using the (map (range min max) f) idiom, or flatten your lists using the flatten function",
            .{max_depth},
        );
    }
    if (val != .ref) return notAScribble(vm, val);

    switch (val.ref.*) {
        .scribble => {
            try canvas.begin();

            switch (val.ref.scribble.shape) {
                .point => |point| try canvas.line(point, point),
                .line => |line| try canvas.line(line.start, line.end),
                .rect => |rect| try canvas.rect(rect.top_left, rect.size),
                .circle => |circle| try canvas.circle(circle.center, circle.radius),
            }

            switch (val.ref.scribble.action) {
                .stroke => |stroke| try canvas.stroke(value.rgbaTo8(stroke.color), stroke.thickness),
                .fill => |fill| try canvas.fill(value.rgbaTo8(fill.color)),
            }
        },

        .list => {
            for (val.ref.list) |nested| {
                try vm.consumeFuel(&vm.fuel, 1);
                try renderRec(vm, canvas, nested, depth + 1, max_depth);
            }
        },

        .shape => {
            return vm.throw("the brush returned a bare shape, which cannot be drawn. try wrapping your shape in a fill or a stroke: (fill #000 <shape>)", .{});
        },

        else => return notAScribble(vm, val),
    }
}

pub fn render(vm: *Vm, canvas: *Canvas, max_depth: usize) !void {
    const val = vm.top();
    try renderRec(vm, canvas, val, 0, max_depth);
}
