const std = @import("std");
const protobuf = @import("protobuf");
const kindle = @import("kindle");

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

    const server_step = b.step("server", "build server");
    const client_step = b.step("client", "build client");
    const render_step = b.step("render", "build render application");

    b.default_step.dependOn(server_step);
    b.default_step.dependOn(client_step);
    b.default_step.dependOn(render_step);

    const clap_dep = b.dependency("clap", .{});
    const zap_dep = b.dependency("zap", .{});
    const protobuf_dep = b.dependency("protobuf", .{});
    const zigimg_dep = b.dependency("zigimg", .{});
    const wardrobe_dep = b.dependency("wardrobe", .{});
    const temp_dep = b.dependency("temp", .{});
    const zdt_dep = b.dependency("zdt", .{});

    const gen_proto = b.step("gen-proto", "generates zig files from protocol buffer definitions");
    const protc_step = protobuf.RunProtocStep.create(b, protobuf_dep.builder, target, .{
        .destination_directory = b.path("src/eink_feed_protocol/proto"),
        .source_files = &.{
            "protocol/eink_feed.proto",
        },
        .include_directories = &.{},
    });
    gen_proto.dependOn(&protc_step.step);

    const utils_mod = b.createModule(.{
        .root_source_file = b.path("src/utils/root.zig"),
    });

    const protocol = blk: {
        const mod = b.addModule("eink_feed_protocol", .{
            .root_source_file = b.path("src/eink_feed_protocol/root.zig"),
        });
        mod.addImport("protobuf", protobuf_dep.module("protobuf"));

        break :blk mod;
    };

    const server_mod = b.addModule("eink_feed_server", .{
        .root_source_file = b.path("src/eink_feed_server/root.zig"),
        .target = target,
    });
    server_mod.addImport("utils", utils_mod);
    server_mod.addImport("eink_feed_protocol", protocol);
    server_mod.addImport("zap", zap_dep.module("zap"));
    server_mod.addImport("zigimg", zigimg_dep.module("zigimg"));

    {
        const server_exe = b.addExecutable(.{
            .name = "eink-feed-server",
            .root_source_file = b.path("src/eink_feed_server/main.zig"),
            .target = target,
            .optimize = optimize,
        });
        server_exe.root_module.addImport("eink_feed_server", server_mod);

        server_exe.root_module.addImport("clap", clap_dep.module("clap"));

        const server_exe_install = b.addInstallArtifact(server_exe, .{});
        server_step.dependOn(&server_exe_install.step);
    }

    {
        const libhoudini = buildLibhoudinie(b, target, optimize);

        const render_mod = b.addModule("eink_feed_render", .{
            .root_source_file = b.path("src/eink_feed_render/root.zig"),
            .target = target,
        });
        render_mod.addImport("utils", utils_mod);
        render_mod.addImport("eink_feed_server", server_mod);
        render_mod.addImport("eink_feed_protocol", protocol);
        render_mod.addImport("zigimg", zigimg_dep.module("zigimg"));
        render_mod.addImport("wardrobe", wardrobe_dep.module("wardrobe"));
        render_mod.addImport("temp", temp_dep.module("temp"));
        render_mod.addImport("zdt", zdt_dep.module("zdt"));

        render_mod.linkLibrary(libhoudini);
        render_mod.linkSystemLibrary("wkhtmltox", .{});

        const departures_exe = b.addExecutable(.{
            .name = "eink-feed-render-departures",
            .root_source_file = b.path("src/eink_feed_render/main/departures.zig"),
            .target = target,
            .optimize = optimize,
        });
        departures_exe.root_module.addImport("eink_feed_render", render_mod);
        departures_exe.root_module.addImport("clap", clap_dep.module("clap"));
        departures_exe.root_module.addImport("zdt", zdt_dep.module("zdt"));
        departures_exe.linkLibC();

        const render_exe_install = b.addInstallArtifact(departures_exe, .{});
        render_step.dependOn(&render_exe_install.step);

        const spotify_exe = b.addExecutable(.{
            .name = "eink-feed-render-spotify",
            .root_source_file = b.path("src/eink_feed_render/main/spotify.zig"),
            .target = target,
            .optimize = optimize,
        });
        spotify_exe.root_module.addImport("eink_feed_render", render_mod);
        spotify_exe.root_module.addImport("clap", clap_dep.module("clap"));
        spotify_exe.linkLibC();

        const spotify_exe_install = b.addInstallArtifact(spotify_exe, .{});
        render_step.dependOn(&spotify_exe_install.step);
    }

    {
        const kindle_target = b.resolveTargetQuery(kindle.kindle_target_query_musl);
        const kindle_dep = b.dependency("kindle", .{});

        const libzlib = buildLibzlib(b, kindle_target, optimize);
        const libcurl = try buildKindleLibcurl(b, kindle_target, optimize, libzlib);
        const libevdev = buildLibevdev(b, kindle_target, optimize);

        const client_mod = b.addModule("eink_feed_client", .{
            .root_source_file = b.path("src/eink_feed_client/root.zig"),
            .target = kindle_target,
        });
        client_mod.addImport("zigimg", zigimg_dep.module("zigimg"));

        const client_exe = b.addExecutable(.{
            .name = "eink-feed-client",
            .root_source_file = b.path("src/eink_feed_client/main.zig"),
            .target = kindle_target,
            .optimize = optimize,
        });

        // client_exe.root_module.single_threaded = true; // won't run on kindle when enabling threading
        client_exe.root_module.addImport("eink_feed_client", client_mod);
        client_exe.root_module.addImport("zigimg", zigimg_dep.module("zigimg"));
        client_exe.root_module.addImport("clap", clap_dep.module("clap"));

        // client_exe.addLibraryPath(b.path("kindle/sysroot/lib"));
        // client_exe.linkSystemLibrary2("c", .{ .preferred_link_mode = .dynamic });
        // client_exe.addIncludePath(b.path("kindle/sysroot/usr/include"));

        // client_exe.linkLibrary(libgetauxval_backport);

        client_exe.root_module.addImport("kindle", kindle_dep.module("kindle"));

        client_exe.root_module.addImport("eink_feed_protocol", protocol);

        client_exe.linkLibrary(libcurl);
        client_exe.linkLibrary(libevdev);

        const client_exe_install = b.addInstallArtifact(client_exe, .{});
        client_step.dependOn(&client_exe_install.step);
    }
}

fn buildLibzlib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile {
    const libzlib = @import("libs/zlib.zig").create(b, target, optimize);
    return libzlib;
}

fn buildKindleLibcurl(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, libzlib: *std.Build.Step.Compile) !*std.Build.Step.Compile {
    const libmbedtls = @import("libs/mbedtls.zig").create(b, target, optimize);
    const libcurl = try @import("libs/curl.zig").create(b, target, optimize);
    libcurl.linkLibrary(libmbedtls);
    libcurl.linkLibrary(libzlib);

    return libcurl;
}

fn buildLibevdev(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile {
    const libevdev = @import("libs/evdev/root.zig").create(b, target, optimize);
    return libevdev;
}

fn buildLibhoudinie(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile {
    const libhoudini = @import("libs/houdini.zig").create(b, target, optimize);
    return libhoudini;
}
