/// The root of Mezzaluna is, you guessed it, the root of many of the systems mez needs:
///   - Managing outputs
///   -
const Root = @This();

const std = @import("std");
const wl = @import("wayland").server.wl;
const wlr = @import("wlroots");

const Output = @import("Output.zig");
const View =   @import("View.zig");
const Utils =  @import("Utils.zig");

const server = &@import("main.zig").server;
const gpa = std.heap.c_allocator;

xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1,

scene: *wlr.Scene,
waiting_room: *wlr.SceneTree,
scene_output_layout: *wlr.SceneOutputLayout,

output_layout: *wlr.OutputLayout,

pub fn init(self: *Root) void {
  std.log.info("Creating root of mezzaluna\n", .{});

  errdefer Utils.oomPanic();

  const output_layout = try wlr.OutputLayout.create(server.wl_server);
  errdefer output_layout.destroy();

  const scene = try wlr.Scene.create();
  errdefer scene.tree.node.destroy();

  self.* = .{
    .scene = scene,
    .waiting_room = try scene.tree.createSceneTree(),
    .output_layout = output_layout,
    .xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server),
    .scene_output_layout = try scene.attachOutputLayout(output_layout),
  };
}

pub fn deinit(self: *Root) void {
  var it = self.scene.tree.children.iterator(.forward);

  while(it.next()) |node| {
    if(node.data == null) continue;

    const view: *View = @ptrCast(@alignCast(node.data.?));
    view.deinit();
  }

  self.output_layout.destroy();
  self.scene.tree.node.destroy();
}

// Search output_layout's ouputs, and each outputs views
pub fn viewById(self: *Root, id: u64) ?*View {
  var output_it = self.output_layout.outputs.iterator(.forward);

  while(output_it.next()) |o| {
    if(o.output.data == null) continue;
    const output: *Output = @ptrCast(@alignCast(o.output.data.?));
    var node_it = output.layers.content.children.iterator(.forward);

    while(node_it.next()) |node| {
      if(node.data == null) continue;

      const view: *View = @as(*View, @ptrCast(@alignCast(node.data.?)));
      if(view.id == id) return view;
    }
  }

  return null;
}

pub fn outputById(self: *Root, id: u64) ?*Output {
  var it = self.scene.outputs.iterator(.forward);

  while(it.next()) |scene_output| {
    if(scene_output.output.data == null) continue;

    const output: *Output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?)));
    if(output.id == id) return output;
  }

  return null;
}
