pub const Events = @This();

const std = @import("std");

const Hook = @import("hook.zig");

const Node = struct {
  hook: *const Hook,
  node: std.SinglyLinkedList.Node,
};

events: std.StringHashMap(*std.SinglyLinkedList),
allocator: std.mem.Allocator,

pub fn init(allocator: std.mem.Allocator) !Events {
  return Events{
    .allocator = allocator,
    .events = .init(allocator),
  };
}

pub fn put(self: *Events, key: []const u8, hook: *const Hook) !void {
  var ll: *std.SinglyLinkedList = undefined;
  if (self.events.get(key)) |sll| {
    ll = sll;
  } else {
    ll = try self.allocator.create(std.SinglyLinkedList);
    try self.events.put(key, ll);
    if (self.events.get(key)) |sll| {
      ll = sll;
    }
  }
  const data = try self.allocator.create(Node);
  data.* = .{
    .hook = hook,
    .node = .{},
  };
  ll.prepend(&data.node);
}

// TODO: figure out deletion
// pub fn del(self: *Events, key: ???) !void {}

pub fn exec(self: *Events, event: []const u8, args: anytype) void {
  if (self.events.get(event)) |e| {
    var node = e.first;
    while (node) |n| : (node = n.next) {
      const data: *Node = @fieldParentPtr("node", n);
      data.hook.callback(args);

      // FIXME: not sure why but for some reason our ll doesn't seem to want to
      // admit that there's nothing after the first node.
      break;
    }
  }
}
