const std = @import("std");

pub const mpsc = @import("mpsc.zig");
pub const msg_queue = @import("msg_queue/root.zig");
pub const dispatchers = @import("dispatchers/root.zig");

pub const ResultArena = @import("result.zig").ResultArena;

pub const models = @import("models/root.zig");
pub const web = @import("web/root.zig");

const log = std.log.scoped(.eink_feed_server);

pub const Config = struct {
    pub const Client = struct {
        name: []const u8,
        channel_id: models.Channel.Id,
    };

    web_port: u16,
    channels: []web.api.models.Channel,
    clients: []Client,
};

pub const ConfigError = error{
    InvalidChannelName,
    InvalidClientName,
};

fn dispatcherWrapper(dispatcher: *dispatchers.FrameUpdate) void {
    dispatcher.start() catch unreachable;
}

pub fn run(allocator: std.mem.Allocator, config: *const Config) !void {
    const capacity: usize = 1024;
    var queue = try msg_queue.MsgQueueManaged(ResultArena(models.FrameUpdateMsg))
        .init(allocator, capacity);
    defer queue.deinit();

    var channels_store = web.stores.Channels.init(allocator, queue.unmanaged());
    defer channels_store.deinit();

    for (config.channels) |channel| {
        if (channel.name.len > models.Channel.NameMaxLen)
            return ConfigError.InvalidChannelName;

        var name: [models.Channel.NameMaxLen]u8 = undefined;
        std.mem.copyBackwards(u8, &name, channel.name);

        _ = try channels_store.add(&.{
            .id = channel.id,
            .name = name,
            .name_len = channel.name.len,
            .display = .{
                .width = channel.display.width,
                .height = channel.display.height,
                .orientation = channel.display.orientation.toInternal(),
            },
        });
    }

    for (config.clients) |client| {
        if (client.name.len > models.DisplayClient.NameMaxLen)
            return ConfigError.InvalidClientName;

        var name: [models.DisplayClient.NameMaxLen]u8 = undefined;
        std.mem.copyBackwards(u8, &name, client.name);

        try channels_store.clients.add(&.{
            .name = name,
            .name_len = client.name.len,
            .channel_id = client.channel_id,
        });
    }

    var dispatcher = dispatchers.FrameUpdate.init(
        allocator,
        &.{ .capacity = capacity },
        queue.unmanaged(),
    );
    log.info("Starting dispatcher", .{});
    _ = try std.Thread.spawn(.{}, dispatcherWrapper, .{&dispatcher});

    try web.start(allocator, config.web_port, &channels_store);
}
