const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "wheel",
        .target = target,
        .optimize = optimize,
    });
    exe.linkLibC();
    b.installArtifact(exe);

    const csources = get_project_filepaths(b, "src", ".c") catch unreachable;
    defer csources.deinit();

    exe.addCSourceFiles(csources.items, &.{ "-Wall", "-Wextra", "-pedantic", "--debug", "-Werror", "-Wno-unused-parameter" });

    const fs_obj = b.addObject(.{
        .name = "fs",
        .root_source_file = .{ .path = "src/fs.zig" },
        .target = target,
        .optimize = optimize,
    });
    fs_obj.linkLibC();
    fs_obj.addIncludePath("deps/cglm/include");
    // fs_obj.emit_h = true;
    exe.step.dependOn(&fs_obj.step);
    exe.addObject(fs_obj);

    addSDL2(b, exe);
    addFreetype(b, exe);
    addGlad(b, exe);
    exe.addIncludePath("deps/cglm/include");

    b.installBinFile("assets/Hack Regular Nerd Font Complete.ttf", "assets/Hack Regular Nerd Font Complete.ttf");

    const shaders = get_project_filepaths(b, "assets/shaders", null) catch unreachable;
    defer shaders.deinit();

    const icons = get_project_filepaths(b, "assets/icons", null) catch unreachable;
    defer icons.deinit();

    installBinFiles(b, shaders.items, "assets/shaders") catch unreachable;
    installBinFiles(b, icons.items, "assets/icons") catch unreachable;

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}

fn installBinFiles(b: *std.Build, src_paths: [][]const u8, dest_relative_path: []const u8) !void {
    for (src_paths) |src| {
        const dest = try std.fs.path.join(b.allocator, &[_][]const u8{ dest_relative_path, std.fs.path.basename(src) });
        b.installBinFile(src, dest);
    }
}

fn get_project_filepaths(b: *std.Build, sub_dir: []const u8, extension: ?[]const u8) !std.ArrayList([]const u8) {
    return get_filepaths(b, b.pathFromRoot(sub_dir), extension);
}

fn get_filepaths(b: *std.Build, search_dir: []const u8, extension: ?[]const u8) !std.ArrayList([]const u8) {
    var paths = std.ArrayList([]const u8).init(b.allocator);

    var dir = try std.fs.cwd().openIterableDir(search_dir, .{ .access_sub_paths = true });
    defer dir.close();

    var walker = try dir.walk(b.allocator);
    defer walker.deinit();

    while (try walker.next()) |entry| {
        if (entry.kind != .File) continue;

        if (extension) |ext| {
            if (!std.mem.eql(u8, std.fs.path.extension(entry.basename), ext))
                continue;
        }

        var path = try std.fs.path.join(b.allocator, &.{ search_dir, b.dupe(entry.path) });
        try paths.append(path);
    }

    return paths;
}

fn addSDL2(b: *std.Build, step: *std.Build.LibExeObjStep) void {
    step.addIncludePath("deps/SDL2/include");
    step.addLibraryPath("deps/SDL2");
    step.linkSystemLibrary("SDL2");

    // @Todo: dynamic lib crossplatform support
    b.installBinFile("deps/SDL2/SDL2.dll", "SDL2.dll");
}

const glad_path = "deps/glad";
fn addGlad(b: *std.Build, step: *std.Build.LibExeObjStep) void {
    const glad = build_glad(b, step.optimize, step.target);
    step.linkLibrary(glad);
    step.addIncludePath(glad_path);
}

fn build_glad(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *std.Build.LibExeObjStep {
    const lib = b.addStaticLibrary(.{
        .name = "glad",
        .target = target,
        .optimize = optimize,
    });
    lib.linkLibC();
    lib.addIncludePath(glad_path);

    const glad_src = get_project_filepaths(b, glad_path, ".c") catch unreachable;
    lib.addCSourceFiles(glad_src.items, &.{});
    glad_src.deinit();

    return lib;
}

fn addFreetype(b: *std.Build, step: *std.Build.LibExeObjStep) void {
    const ft = b.anonymousDependency("deps/freetype", @import("deps/freetype/build.zig"), .{});
    step.linkLibrary(ft.artifact("freetype"));
    step.addIncludePath("deps/freetype/include");
}