const std = @import("std");
const vaxis = @import("vaxis");
const vxfw = vaxis.vxfw;

const Text = vxfw.Text;
const ListView = vxfw.ListView;
const Widget = vxfw.Widget;

const Model = struct {
    list_view: ListView,

    pub fn widget(self: *Model) Widget {
        return .{
            .userdata = self,
            .eventHandler = Model.typeErasedEventHandler,
            .drawFn = Model.typeErasedDrawFn,
        };
    }

    pub fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
        const self: *Model = @ptrCast(@alignCast(ptr));
        try ctx.requestFocus(self.list_view.widget());
        switch (event) {
            .key_press => |key| {
                if (key.matches('q', .{}) or key.matchExact('c', .{ .ctrl = true })) {
                    ctx.quit = true;
                    return;
                }
            },
            else => {},
        }
    }

    fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface {
        const self: *Model = @ptrCast(@alignCast(ptr));
        const max = ctx.max.size();

        const list_view: vxfw.SubSurface = .{
            .origin = .{ .row = 1, .col = 1 },
            .surface = try self.list_view.draw(ctx),
        };

        const children = try ctx.arena.alloc(vxfw.SubSurface, 1);
        children[0] = list_view;

        return .{
            .size = max,
            .widget = self.widget(),
            .buffer = &.{},
            .children = children,
        };
    }
};

pub fn main(init: std.process.Init) !void {
    const io = init.io;
    const alloc = init.gpa;

    var buffer: [1024]u8 = undefined;
    var app: vxfw.App = try .init(io, alloc, init.environ_map, &buffer);
    defer app.deinit();

    const model = try alloc.create(Model);
    defer alloc.destroy(model);

    const n = 80;
    var texts: std.ArrayList(Widget) = try .initCapacity(alloc, n);
    var allocs: std.ArrayList(*Text) = try .initCapacity(alloc, n);
    defer {
        for (allocs.items) |tw| {
            alloc.free(tw.text);
            alloc.destroy(tw);
        }
        allocs.deinit(alloc);
        texts.deinit(alloc);
    }

    for (0..n) |i| {
        const t = std.fmt.allocPrint(alloc, "List Item {d} of {d}", .{ i, n }) catch "placeholder";
        const tw = try alloc.create(Text);
        tw.* = .{ .text = t };
        _ = try allocs.append(alloc, tw);
        _ = try texts.append(alloc, tw.widget());
    }

    model.* = .{
        .list_view = .{
            .wheel_scroll = 3,
            .scroll = .{
                .wants_cursor = true,
            },
            .children = .{ .slice = texts.items },
        },
    };

    try app.run(model.widget(), .{});
}

test {
    std.testing.refAllDecls(@This());
}
