#include <hilbert/kernel/application.hpp>
#include <hilbert/kernel/framebuffer.hpp>
#include <hilbert/kernel/load-app.hpp>
#include <hilbert/kernel/paging.hpp>
#include <hilbert/kernel/input.hpp>
#include <hilbert/kernel/panic.hpp>
#include <hilbert/kernel/vfile.hpp>

namespace hilbert::kernel::syscall {

  enum file_result : uint64_t {
    file_result_success,
    file_result_bad_file_handle,
    file_result_device_error,
    file_result_file_system_corrupt,
    file_result_out_of_bounds,
    file_result_does_not_exist,
    file_result_directory
  };

  void set_zero(uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
    rax = 0;
    rdi = 0;
    rsi = 0;
    rdx = 0;
  }

  void encode_color_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    rax = (uint64_t)framebuffer::encode_color(
      rdi & 0xff, (rdi >> 8) & 0xff, (rdi >> 16) & 0xff);
    rdi = 0;
    rsi = 0;
    rdx = 0;

  }

  void get_framebuffer_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    auto *process = application::running_thread->owner;
    if (process->framebuffer_vaddr == 0) {
      uint64_t pages_needed =
        (framebuffer::dword_pitch * framebuffer::height * 4 - 1) / 4096 + 1;
      uint64_t vaddr = process->memory->get_free_vaddr_pages(pages_needed);
      for (uint64_t i = 0; i < pages_needed; ++i)
        process->memory->map_page(
          vaddr + i * 4096, framebuffer::paddr + i * 4096, true, false, false);
      process->framebuffer_vaddr = vaddr;
    }

    rax = process->framebuffer_vaddr;
    rdi =
      (uint64_t)(uint32_t)framebuffer::width |
      ((uint64_t)(uint32_t)framebuffer::height << 32);
    rsi = (uint32_t)framebuffer::dword_pitch;
    rdx = 0;

  }

  void open_file_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    const char *path = (const char *)rdi;
    uint64_t path_length = rsi;
    bool allow_creation = rdx & 1;
    bool only_allow_creation = rdx & 2;

    set_zero(rax, rdi, rsi, rdx);

    auto *process = application::running_thread->owner;
    if (!process->memory->valid_to_read(path, path + path_length, false))
      return;

    utility::string path_string(path, path_length);

    vfile::canon_path cp;
    vfile::vfile file;
    vfile::canonize_path(path_string, cp);

    switch (vfile::look_up_path(cp, file, true)) {

    case storage::fs_result::device_error:
    case storage::fs_result::fs_corrupt:

      rax = (uint64_t)application::stream_result::io_error;
      return;

    case storage::fs_result::does_not_exist:

      if (!allow_creation) {
        rax = (uint64_t)application::stream_result::does_not_exist;
        return;
      }

      //TODO: create the file
      panic(0x9af5e6);

    case storage::fs_result::success:

      if (only_allow_creation) {
        rax = (uint64_t)application::stream_result::already_exists;
        return;
      }

      if (file.dir_entry.type != storage::file_type::regular_file) {
        rax = (uint64_t)application::stream_result::not_a_regular_file;
        return;
      }

      rdi = process->add_file_stream(new application::file_stream {
        .the_file = utility::move(file), .offset = 0 });
      rax = (uint64_t)application::stream_result::success;

    }

  }

  void end_this_thread_syscall(
    uint64_t &, uint64_t &rdi, uint64_t &, uint64_t &) {

    int exit_code = (int)(uint32_t)rdi;
    auto *t = application::running_thread;
    t->on_end_thread();
    t->owner->notify_thread_ended(t, exit_code);
    delete t;
    application::resume_next_thread();
  }

  void get_new_pages_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    uint64_t count = rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto *p = application::running_thread->owner;
    uint64_t vaddr = p->memory->get_free_vaddr_pages(count);

    for (uint64_t i = 0; i < count; ++i) {
      uint64_t kvaddr;
      uint64_t paddr;
      paging::map_new_kernel_page(kvaddr, paddr);
      for (int i = 0; i < 4096; ++i)
        ((uint8_t *)kvaddr)[i] = 0;
      paging::unmap_kernel_page((uint64_t)kvaddr);
      p->memory->map_page(vaddr + i * 4096, paddr, true, false, true);
    }

    rax = vaddr;

  }

  void get_input_packet_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    set_zero(rax, rdi, rsi, rdx);

    while (input::input_queue->count == 0)
      application::running_thread->wait_for_input();

    input::input_packet packet = input::input_queue->take();
    if (packet.is_mouse) {
      rax = packet.mouse.buttons | 0x80;
      rdi = (uint16_t)packet.mouse.x_change;
      rsi = (uint16_t)packet.mouse.y_change;
    }
    else {
      rax = 0;
      rdi = packet.keyboard;
    }

  }

  void create_socket(
    application::process *p1, application::process *p2,
    application::socket_stream_end *&se1_out,
    application::socket_stream_end *&se2_out) {

    application::socket *s = new application::socket();
    se1_out = new application::socket_stream_end {
      .the_socket = s, .read_queue = s->queue_1, .write_queue = s->queue_2,
      .waiting_to_read = utility::queue<application::thread *>(),
      .is_other_side_open = true, .other_process = p2, .other_end = 0 };
    se2_out = new application::socket_stream_end {
      .the_socket = s, .read_queue = s->queue_2, .write_queue = s->queue_1,
      .waiting_to_read = utility::queue<application::thread *>(),
      .is_other_side_open = true, .other_process = p1, .other_end = se1_out };
    se1_out->other_end = se2_out;

  }

  void create_private_socket_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    set_zero(rax, rdi, rsi, rdx);

    auto *p = application::running_thread->owner;

    application::socket_stream_end *se1, *se2;
    create_socket(p, p, se1, se2);

    rax = p->add_socket_stream(se1);
    rdi = p->add_socket_stream(se2);

  }

  void create_socket_listener_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    auto *p = application::running_thread->owner;

    const char *id = (const char *)rdi;
    uint64_t id_length = rsi;
    set_zero(rax, rdi, rsi, rdx);

    if (!p->memory->valid_to_read(id, id + id_length, false))
      return;

    auto *sl =
      application::try_register_socket_listener(
        utility::string(id, id_length));

    if (sl) {
      rdx = p->add_socket_listener(sl);
      rax = (uint64_t)application::stream_result::success;
    }

    else
      rax = (uint64_t)application::stream_result::socket_id_already_in_use;

  }

  void stop_socket_listener_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto *sl =
      application::running_thread->owner->take_socket_listener(handle);

    if (!sl)
      return;

    while (sl->waiting_to_accept.count > 0)
      sl->waiting_to_accept.take()->notify_no_socket_stream();
    while (sl->waiting_to_connect.count > 0)
      sl->waiting_to_connect.take()->notify_no_socket_stream();

    application::remove_socket_listener(sl);

  }

  void accept_socket_connection_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;
    auto *sl = t->owner->get_socket_listener(handle);

    if (!sl) {
      rax = (uint64_t)application::stream_result::bad_handle;
      return;
    }

    if (sl->waiting_to_connect.count > 0) {

      auto *ot = sl->waiting_to_connect.take();

      application::socket_stream_end *our_end, *their_end;
      create_socket(t->owner, ot->owner, our_end, their_end);

      unsigned our_handle = t->owner->add_socket_stream(our_end);
      unsigned their_handle = ot->owner->add_socket_stream(their_end);

      ot->notify_new_socket_stream(their_handle);

      rax = (uint64_t)application::stream_result::success;
      rdi = our_handle;

    }

    else {

      auto result = t->wait_to_accept_from(sl);
      if (result.has_value) {
        rax = (uint64_t)application::stream_result::success;
        rdi = result.value;
      }
      else
        rax = (uint64_t)application::stream_result::socket_listener_closed;

    }

  }

  void connect_to_socket_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    const char *id = (const char *)rdi;
    uint64_t id_length = rsi;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;

    if (!t->owner->memory->valid_to_read(id, id + id_length, false))
      return;

    utility::string id_string(id, id_length);
    auto *sl = application::try_get_socket_listener(id_string);

    if (!sl) {
      rax = (uint64_t)application::stream_result::socket_id_not_in_use;
      return;
    }

    if (sl->waiting_to_accept.count > 0) {

      auto *ot = sl->waiting_to_accept.take();

      application::socket_stream_end *our_end, *their_end;
      create_socket(t->owner, ot->owner, our_end, their_end);

      unsigned our_handle = t->owner->add_socket_stream(our_end);
      unsigned their_handle = ot->owner->add_socket_stream(their_end);

      ot->notify_new_socket_stream(their_handle);

      rax = (uint64_t)application::stream_result::success;
      rdi = our_handle;

    }

    else {

      auto result = t->wait_to_connect_to(sl);
      if (result.has_value) {
        rax = (uint64_t)application::stream_result::success;
        rdi = result.value;
      }
      else
        rax = (uint64_t)application::stream_result::socket_listener_closed;

    }

  }

  void close_stream_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    set_zero(rax, rdi, rsi, rdx);
    application::running_thread->owner->close_stream(handle);

  }

  void seek_stream_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    uint8_t origin = (uint8_t)rsi;
    int64_t offset = (int64_t)rdx;
    set_zero(rax, rdi, rsi, rdx);

    if (origin >= 3)
      return;

    auto stream = application::running_thread->owner->get_stream(handle);

    if (stream.is_null()) {
      rax = (uint64_t)application::stream_result::bad_handle;
      return;
    }

    if (stream.is_socket) {
      rax = (uint64_t)application::stream_result::not_seekable;
      return;
    }

    auto *fs = stream.as_file_stream;

    switch (origin) {

    case 0://beginning
      if (offset < 0 || (uint64_t)offset > fs->the_file.dir_entry.length) {
        rax = (uint64_t)application::stream_result::out_of_bounds;
        return;
      }
      fs->offset = offset;
      rax = (uint64_t)application::stream_result::success;
      return;

    case 1://end
      if (offset > 0 || (uint64_t)-offset > fs->the_file.dir_entry.length) {
        rax = (uint64_t)application::stream_result::out_of_bounds;
        return;
      }
      fs->offset = fs->the_file.dir_entry.length + offset;
      rax = (uint64_t)application::stream_result::success;
      return;

    case 2://current position
      int64_t new_offset = offset + fs->offset;
      if (new_offset < 0 || (uint64_t)new_offset > fs->the_file.dir_entry.length) {
        rax = (uint64_t)application::stream_result::out_of_bounds;
        return;
      }
      fs->offset = (uint64_t)new_offset;
      rax = (uint64_t)application::stream_result::success;
      return;

    }

  }

  void read_from_stream_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    uint64_t count = (uint64_t)rsi;
    uint8_t *buffer = (uint8_t *)rdx;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;

    if (!t->owner->memory->valid_to_read(buffer, buffer + count, true))
      return;

    auto stream = t->owner->get_stream(handle);

    if (stream.is_null()) {
      rax = (uint64_t)application::stream_result::bad_handle;
      return;
    }

    if (stream.is_socket) {
      auto *ss = stream.as_socket_stream;
      for (uint64_t i = 0; i < count; ++i) {
        while (ss->read_queue.count == 0) {
          if (!ss->is_other_side_open) {
            rax = (uint64_t)application::stream_result::other_end_closed;
            return;
          }
          t->wait_for_socket_stream(ss);
        }
        buffer[i] = ss->read_queue.take();
      }
      rax = (uint64_t)application::stream_result::success;
    }

    else {
      auto *fs = stream.as_file_stream;
      if (fs->offset + count > fs->the_file.dir_entry.length) {
        rax = (uint64_t)application::stream_result::out_of_bounds;
        return;
      }
      auto read_result = fs->the_file.read_file(fs->offset, count, buffer);
      if (read_result != storage::fs_result::success) {
        rax = (uint64_t)application::stream_result::io_error;
        return;
      }
      fs->offset += count;
      rax = (uint64_t)application::stream_result::success;
    }

  }

  void write_to_stream_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    uint64_t count = (uint64_t)rsi;
    const uint8_t *buffer = (uint8_t *)rdx;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;

    if (!t->owner->memory->valid_to_read(buffer, buffer + count, false))
      return;

    auto stream = t->owner->get_stream(handle);

    if (stream.is_null()) {
      rax = (uint64_t)application::stream_result::bad_handle;
      return;
    }

    if (stream.is_socket) {
      auto *ss = stream.as_socket_stream;
      if (!ss->is_other_side_open) {
        rax = (uint64_t)application::stream_result::other_end_closed;
        return;
      }
      auto &wtr_queue = ss->other_end->waiting_to_read;
      for (uint64_t i = 0; i < count; ++i) {
        ss->write_queue.insert(buffer[i]);
        if (wtr_queue.count > 0)
          application::paused_threads->insert(wtr_queue.take());
      }
      rax = (uint64_t)application::stream_result::success;
    }

    else {
      //TODO: write to file
      panic(0x9af5e6);
    }

  }

  void get_stream_length_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;

    auto stream = t->owner->get_stream(handle);

    if (stream.is_null())
      rax = (uint64_t)application::stream_result::bad_handle;

    else if (stream.is_socket)
      rax = (uint64_t)application::stream_result::not_sized;

    else {
      rdi = stream.as_file_stream->the_file.dir_entry.length;
      rax = (uint64_t)application::stream_result::success;
    }

  }

  struct env_var_spec {
    uint64_t name_len;
    const char *name;
    uint64_t value_len;
    const char *value;
  };

  struct gstream_spec {
    uint64_t parent_handle;
    uint64_t child_handle;
  };

  struct psi_spec {
    uint64_t path_len;
    const char *path;
    uint64_t env_var_count;
    const env_var_spec *env_vars;
    uint64_t gstream_count;
    const gstream_spec *gstreams;
  };

  void start_process_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    const psi_spec *psi = (const psi_spec *)rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto *owner = application::running_thread->owner;

    if (!owner->memory->valid_to_read(psi, psi + 1, false) ||
        !owner->memory->valid_to_read(
          psi->path, psi->path + psi->path_len, false) ||
        !owner->memory->valid_to_read(
          psi->env_vars, psi->env_vars + psi->env_var_count, false) ||
        !owner->memory->valid_to_read(
          psi->gstreams, psi->gstreams + psi->gstream_count, false))
      return;

    for (uint64_t i = 0; i < psi->env_var_count; ++i)
      if (!owner->memory->valid_to_read(psi->env_vars[i].name,
            psi->env_vars[i].name + psi->env_vars[i].name_len, false) ||
          !owner->memory->valid_to_read(psi->env_vars[i].value,
            psi->env_vars[i].value + psi->env_vars[i].value_len, false))
        return;

    for (uint64_t i = 0; i < psi->gstream_count; ++i) {
      auto owner_stream = owner->get_stream(psi->gstreams[i].parent_handle);
      if (owner_stream.is_null() || (owner_stream.is_socket &&
            owner_stream.as_socket_stream->waiting_to_read.count != 0) ||
          psi->gstreams[i].child_handle >= 65536)
        return;
    }

    utility::string path_string(psi->path, psi->path_len);
    vfile::canon_path cpath;
    vfile::canonize_path(path_string, cpath);

    vfile::vfile file;
    auto lookup_result = vfile::look_up_path(cpath, file, true);
    switch (lookup_result) {
    case storage::fs_result::does_not_exist:
      rax = (uint64_t)application::stream_result::does_not_exist;
      return;
    case storage::fs_result::device_error:
    case storage::fs_result::fs_corrupt:
      rax = (uint64_t)application::stream_result::io_error;
      return;
    case storage::fs_result::success:
      break;
    }

    app_memory *memory = new app_memory();
    uint64_t entry_point;
    load_app_result load_result = load_app(file, *memory, entry_point);

    switch (load_result) {
    case load_app_result::io_error:
      rax = (uint64_t)application::stream_result::io_error;
      delete memory;
      return;
    case load_app_result::not_app:
      rax = (uint64_t)application::stream_result::not_an_executable;
      delete memory;
      return;
    case load_app_result::success:
      break;
    }

    application::process *p =
      new application::process(memory, file.dir_entry.name);

    for (uint64_t i = 0; i < psi->env_var_count; ++i)
      p->add_environment_variable(
        utility::string(psi->env_vars[i].name, psi->env_vars[i].name_len),
        utility::string(psi->env_vars[i].value, psi->env_vars[i].value_len));

    for (uint64_t i = 0; i < psi->gstream_count; ++i) {
      auto s = owner->take_stream(psi->gstreams[i].parent_handle);
      if (s.is_socket && s.as_socket_stream->is_other_side_open)
        s.as_socket_stream->other_end->other_process = p;
      p->add_stream_with_handle(psi->gstreams[i].child_handle, s);
    }

    rax = (uint64_t)application::stream_result::success;
    rdi = application::add_process(p);
    application::thread *t = new application::thread(p, entry_point);
    p->add_thread(t);
    application::paused_threads->insert(t);

  }

  void end_this_process_syscall(
    uint64_t &, uint64_t &rdi, uint64_t &, uint64_t &) {

    int exit_code = (int32_t)(uint32_t)rdi;
    auto *p = application::running_thread->owner;
    p->on_end_process(exit_code);
    application::resume_next_thread();

  }

  void set_stream_length_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;

    auto stream = t->owner->get_stream(handle);

    if (stream.is_null())
      rax = (uint64_t)application::stream_result::bad_handle;

    else if (stream.is_socket)
      rax = (uint64_t)application::stream_result::not_sized;

    else {
      //TODO
      panic(0x9af5e6);
    }

  }

  void get_other_end_process_handle_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;

    auto stream = t->owner->get_stream(handle);

    if (stream.is_null())
      rax = (uint64_t)application::stream_result::bad_handle;

    else if (!stream.is_socket)
      rax = (uint64_t)application::stream_result::other_end_closed;

    else {
      auto s = stream.as_socket_stream;
      if (!s->is_other_side_open)
        rax = (uint64_t)application::stream_result::other_end_closed;
      else {
        rax = (uint64_t)application::stream_result::success;
        rdi = (uint64_t)s->other_process->id;
      }
    }

  }

  void start_thread_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    uint64_t entry_point = rdi;
    uint64_t argument = rsi;
    set_zero(rax, rdi, rsi, rdx);

    auto *p = application::running_thread->owner;
    auto *t = new application::thread(p, entry_point);
    t->saved_state.rdi = argument;
    p->add_thread(t);
    application::paused_threads->insert(t);

  }

  void clear_socket_read_queue_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    unsigned handle = (unsigned)rdi;
    set_zero(rax, rdi, rsi, rdx);

    auto stream = application::running_thread->owner->get_stream(handle);

    if (stream.is_null() || !stream.is_socket) {
      rax = 0;
      return;
    }

    auto &queue = stream.as_socket_stream->read_queue;

    rax = queue.count;
    queue.clear();

  }

  void get_environment_variable_length_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    const char *name = (const char *)rdi;
    uint64_t name_len = rsi;
    set_zero(rax, rdi, rsi, rdx);

    auto *app = application::running_thread->owner;

    if (!app->memory->valid_to_read(name, name + name_len, false))
      return;

    utility::string name_string(name, name_len);
    utility::string *value = app->get_environment_variable(name_string);
    rax = value == 0 ? (uint64_t)-1 : value->count;

  }

  void get_environment_variable_value_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    const char *name = (const char *)rdi;
    uint64_t name_len = rsi;
    char *buffer = (char *)rdx;
    set_zero(rax, rdi, rsi, rdx);

    auto *app = application::running_thread->owner;

    if (!app->memory->valid_to_read(name, name + name_len, false))
      return;

    utility::string name_string(name, name_len);
    utility::string *value = app->get_environment_variable(name_string);
    if (value == 0)
      return;

    if (!app->memory->valid_to_read(buffer, buffer + value->count, true))
      return;

    for (unsigned i = 0; i < value->count; ++i)
      buffer[i] = value->buffer[i];

  }

  void set_thread_name_syscall(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

    const char *name = (const char *)rdi;
    uint64_t name_len = rsi;
    set_zero(rax, rdi, rsi, rdx);

    auto *t = application::running_thread;

    if (!t->owner->memory->valid_to_read(name, name + name_len, false))
      return;

    t->name = utility::string(name, name_len);

  }

  void (*handlers[])(
    uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) = {

    &encode_color_syscall,
    &get_framebuffer_syscall,
    &open_file_syscall,
    &end_this_thread_syscall,
    &get_new_pages_syscall,
    &get_input_packet_syscall,
    &create_private_socket_syscall,
    &create_socket_listener_syscall,
    &stop_socket_listener_syscall,
    &accept_socket_connection_syscall,
    &connect_to_socket_syscall,
    &close_stream_syscall,
    &seek_stream_syscall,
    &read_from_stream_syscall,
    &write_to_stream_syscall,
    &get_stream_length_syscall,
    &start_process_syscall,
    &end_this_process_syscall,
    &set_stream_length_syscall,
    &get_other_end_process_handle_syscall,
    &start_thread_syscall,
    &clear_socket_read_queue_syscall,
    &get_environment_variable_length_syscall,
    &get_environment_variable_value_syscall,
    &set_thread_name_syscall

  };

  static constexpr int max_syscall_number = 24;

}

using namespace hilbert::kernel::syscall;

extern "C" void do_syscall(
  uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {

  if (rax <= max_syscall_number && handlers[rax] != 0)
    handlers[rax](rax, rdi, rsi, rdx);
  else
    set_zero(rax, rdi, rsi, rdx);

}