const std = @import("std");
const Action = @import("../actions.zig").Action;

pub const IOInterface = struct {
    ptr: *anyopaque,
    displayMessageFn: *const fn (ptr: *anyopaque, str: []const u8) anyerror!void,
    waitForTickFn: *const fn (ptr: *anyopaque) anyerror!Action,
    deinitFn: *const fn (ptr: *anyopaque) void,

    pub fn init(ptr: anytype) !IOInterface {
        const T = @TypeOf(ptr);
        const ptr_info = @typeInfo(T);

        const gen = struct {
            pub fn displayMessage(pointer: *anyopaque, str: []const u8) anyerror!void {
                const self: T = @ptrCast(@alignCast(pointer));
                return ptr_info.Pointer.child.displayMessage(self, str);
            }

            pub fn waitForTick(pointer: *anyopaque) anyerror!Action {
                const self: T = @ptrCast(@alignCast(pointer));
                return ptr_info.Pointer.child.waitForTick(self);
            }

            pub fn deinit(pointer: *anyopaque) void {
                const self: T = @ptrCast(@alignCast(pointer));
                return ptr_info.Pointer.child.deinit(self);
            }
        };

        return .{
            .ptr = ptr,
            .displayMessageFn = gen.displayMessage,
            .waitForTickFn = gen.waitForTick,
            .deinitFn = gen.deinit,
        };
    }

    pub fn displayMessage(self: IOInterface, str: []const u8) anyerror!void {
        return self.displayMessageFn(self.ptr, str);
    }

    pub fn waitForTick(self: IOInterface) anyerror!Action {
        return self.waitForTickFn(self.ptr);
    }

    pub fn deinit(self: IOInterface) void {
        return self.deinitFn(self.ptr);
    }
};