#include <pake/window.hpp>
#include <cassert>

//TODO: handle errors on socket connection, read, and write

namespace pake {

  window::window(int width, int height, const std::string &title)
  : width(width), height(height), contents(width, height), root() {

    euler::syscall::connect_to_socket("hilbert.compositor", socket);

    assert(width >= 0 && height >= 0);

    struct [[gnu::packed]] { uint8_t type; uint32_t width; uint32_t height; }
      dimensions_pkt = {.type = 0x02, .width = (uint32_t)width, .height = (uint32_t)height };
    euler::syscall::write_to_stream(socket, sizeof(dimensions_pkt), &dimensions_pkt);

    assert(title.size() <= UINT32_MAX);

    struct [[gnu::packed]] { uint8_t type; uint32_t length; }
      title_pkt = {.type = 0x03, .length = (uint32_t)title.size() };
    euler::syscall::write_to_stream(socket, sizeof(title_pkt), &title_pkt);
    euler::syscall::write_to_stream(socket, title.size(), title.data());

  }

  window::~window() {
    euler::syscall::close_stream(socket);
  }

  void window::show() {
    uint8_t packet = 0;
    euler::syscall::write_to_stream(socket, 1, &packet);
  }

  void window::hide() {
    uint8_t packet = 1;
    euler::syscall::write_to_stream(socket, 1, &packet);
  }

  void window::set_root(std::unique_ptr<widget> &&w) {
    root = std::move(w);
    root->notify_size(width, height);
    root->render(contents, 0, 0, true);
  }

  void window::render_and_send_to_compositor() {

    root->render(contents, 0, 0, false);
    auto dirties = contents.get_dirty_regions();

    for (auto it = dirties.cbegin(); it != dirties.cend(); ++it) {

      struct [[gnu::packed]] {
        uint8_t type;
        uint32_t start_x; uint32_t start_y;
        uint32_t   width; uint32_t  height;
      } update_pkt = {
        .type = 0x04,
        .start_x = (uint32_t)it->start_x, .start_y = (uint32_t)it->start_y,
        .  width = (uint32_t)it->  width, . height = (uint32_t)it-> height
      };

      euler::syscall::write_to_stream(socket, sizeof(update_pkt), &update_pkt);

      for (int y = it->start_y; y < it->start_y + it->height; ++y)
        euler::syscall::write_to_stream(socket,
          it->width * sizeof(daguerre::hilbert_color),
          &contents.image.at(it->start_x, y));

    }

    contents.clear_dirty();

  }

}
