const std = @import("std");
const r = @import("raylib.zig");

pub const AssetsError = error{
    FileNotFound,
    IsDirectory,
};

const assets = @embedFile("assets");

const allocator = std.heap.page_allocator;

pub fn get(name: []const u8) ![]u8 {
    // TODO: check on filesystem first
    var fbs = std.io.fixedBufferStream(assets);
    var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    var iter = std.tar.iterator(fbs.reader(), .{
        .file_name_buffer = &file_name_buffer,
        .link_name_buffer = &link_name_buffer,
    });
    while (try iter.next()) |file| {
        switch (file.kind) {
            .directory => {
                if (std.mem.eql(u8, file.name, name)) {
                    return error.IsDirectory;
                }
            },
            .file => {
                if (std.mem.eql(u8, file.name, name)) {
                    //std.debug.print("[Assets] {s}: read {} bytes\n", .{ file.name, file.size });
                    return file.reader().readAllAlloc(allocator, file.size);
                }
            },
            .sym_link => {
                if (std.mem.eql(u8, file.name, name)) {
                    const actual = try std.fs.path.resolvePosix(allocator, &.{ file.name, file.link_name });
                    defer free(actual);
                    return get(actual);
                }
            },
        }
    }
    return error.FileNotFound;
}

pub fn free(data: anytype) void {
    allocator.free(data);
}

pub fn getImage(name: []const u8) !r.Image {
    const a = try get(name);
    defer free(a);
    return r.LoadImageFromMemory(std.fs.path.extension(name).ptr, a.ptr, @intCast(a.len));
}

pub fn getTexture(name: []const u8) !r.Texture2D {
    const i = try getImage(name);
    defer r.UnloadImage(i);
    return r.LoadTextureFromImage(i);
}

pub fn getFont(name: []const u8, size: u32) !r.Font {
    const f = try get(name);
    defer free(f);
    return r.LoadFontFromMemory(std.fs.path.extension(name).ptr, f.ptr, @intCast(f.len), @intCast(size), null, 0);
}

pub fn getBitmapFont(name: []const u8, count: r.Vector2, size: r.Vector2, padding: c_int) !r.Font {
    const cnt: usize = @intFromFloat(count.x * count.y);
    return r.Font{
        .baseSize = @intFromFloat(size.y),
        .glyphCount = @intCast(cnt),
        .glyphPadding = padding,
        .texture = try getTexture(name),
        .recs = blk: {
            var recs = try allocator.alloc(r.Rectangle, cnt);
            //defer allocator.free(recs);
            var i: u32 = 0;
            while (i < cnt) : (i += 1) {
                const ii: f32 = @floatFromInt(i);
                recs[i] = r.Rectangle{
                    .x = @mod(ii, count.x) * size.x,
                    .y = @floor(ii / count.x) * size.y,
                    .width = size.x,
                    .height = size.y,
                };
            }
            break :blk recs.ptr;
        },
        .glyphs = blk: {
            var glyphs = try allocator.alloc(r.GlyphInfo, cnt);
            //defer allocator.free(glyphs);
            var i: u32 = 0;
            while (i < cnt) : (i += 1) {
                glyphs[i] = r.GlyphInfo{
                    .value = @intCast(i),
                    .offsetX = 0,
                    .offsetY = 0,
                    .advanceX = @intFromFloat(size.x),
                    .image = r.GenImageColor(@intFromFloat(size.x), @intFromFloat(size.y), r.WHITE),
                };
            }
            break :blk glyphs.ptr;
        },
    };
}
