const std = @import("std");

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

const Clients = @import("Clients.zig");

const Self = @This();

allocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
channels: std.AutoHashMap(models.Channel.Id, *models.Channel),
lock: std.Thread.RwLock = .{},

clients: Clients,

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

pub fn init(allocator: std.mem.Allocator, queue: *msg_queue.MsgQueueUnmanaged(ResultArena(models.FrameUpdateMsg))) Self {
    return .{
        .allocator = allocator,
        .arena = std.heap.ArenaAllocator.init(allocator),
        .channels = std.AutoHashMap(models.Channel.Id, *models.Channel).init(allocator),
        .clients = Clients.init(allocator),
        .queue = queue,
    };
}

pub fn deinit(self: *Self) void {
    // var iter = self.channels.valueIterator();
    // while (iter.next()) |ptr| {
    //     const channel = ptr.*;
    //     self.allocator.free(channel.name);
    //     self.allocator.free(channel.description);
    //     if (channel.frame) |frame| {
    //         self.allocator.free(frame.frame);
    //     }
    //     self.allocator.destroy(channel);
    // }

    self.clients.deinit();
    self.channels.deinit();
    self.arena.deinit();
}

pub fn add(self: *Self, channel: *const models.Channel) !?models.Channel.Id {
    self.lock.lock();
    defer self.lock.unlock();

    if (self.channels.contains(channel.id)) {
        return null;
    }

    const allocator = self.arena.allocator();

    const copy = try allocator.create(models.Channel);
    copy.id = channel.id;
    @memcpy(&copy.name, &channel.name);
    copy.name_len = channel.name_len;
    copy.display = channel.display;

    try self.channels.put(channel.id, copy);
    return channel.id;
}

pub fn get(self: *Self, id: models.Channel.Id) ?models.Channel {
    self.lock.lockShared();
    defer self.lock.unlockShared();

    const channel = self.channels.get(id) orelse return null;

    var copy: models.Channel = undefined;
    copy.id = channel.id;
    @memcpy(&copy.name, &channel.name);
    copy.name_len = channel.name_len;
    copy.display = channel.display;

    return copy;
}

pub fn remove(self: *Self, id: models.Channel.Id) bool {
    self.lock.lock();
    defer self.lock.unlock();

    if (self.channels.fetchRemove(id)) |kv| {
        const allocator = self.arena.allocator();
        const channel = kv.value;
        if (channel.frame) |frame| {
            allocator.free(frame.frame);
        }
        allocator.destroy(channel);
    }

    return false;
}
