const std = @import("std");

const protocol = @import("eink_feed_protocol");

const ResultArena = @import("../result.zig").ResultArena;
const msg_queue = @import("../msg_queue/root.zig");
const models = @import("../models/root.zig");
const web = @import("../web/root.zig");

const WebsocketHandler = web.endpoints.ws.Display.WebsocketHandler;

const Self = @This();

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

/// Frame update dispatcher config
pub const Config = struct {
    /// Queue capacity
    capacity: usize,
};

allocator: std.mem.Allocator,

/// Dispatcher config
config: *const Config,

/// Frame update queue
queue: *msg_queue.MsgQueueUnmanaged(ResultArena(models.FrameUpdateMsg)),

pub fn init(
    allocator: std.mem.Allocator,
    config: *const Config,
    queue: *msg_queue.MsgQueueUnmanaged(ResultArena(models.FrameUpdateMsg)),
) Self {
    return .{
        .allocator = allocator,
        .config = config,
        .queue = queue,
    };
}

pub fn start(self: *Self) !void {
    const buf = try self.allocator.alloc(ResultArena(models.FrameUpdateMsg), self.queue.capacity());
    defer self.allocator.free(buf);

    while (true) {
        const updates = try self.queue.dequeueAll(buf);
        for (updates) |*result| {
            defer result.deinit();
            const update = result.value;

            const min_len: u32 = update.display.height * update.display.width;
            if (update.frame.len < min_len) continue;

            const msg_buf: []const u8 = blk: {
                var frame_msg = protocol.protobuf.FrameMessage.init(self.allocator);
                defer frame_msg.deinit();
                frame_msg.display = update.display.toProtobuf(self.allocator);
                frame_msg.pixels = .{ .Const = update.frame };

                var msg = protocol.protobuf.ServerMessage.init(self.allocator);
                defer msg.deinit();
                msg.frame = frame_msg;
                break :blk try msg.encode(self.allocator);
            };
            defer self.allocator.free(msg_buf);

            const channel_id_str = try std.fmt.allocPrint(self.allocator, "{d}", .{update.channel_id});
            defer self.allocator.free(channel_id_str);

            WebsocketHandler.publish(.{
                .channel = channel_id_str,
                .message = msg_buf,
            });
        }
    }
}
