#include <uv.h>
#include "mavka/biblioteka.h"
#include "mavka/prystriy.h"

// згенеровано glm-5.2

#define MAVKA_NET_HIGH_WATERMARK (16 * 1024)

typedef enum {
  CONN_STATE_DISCONNECTED = 0,
  CONN_STATE_CONNECTING,
  CONN_STATE_CONNECTED,
  CONN_STATE_CLOSING
} MavkaConnState;

typedef struct {
  uv_tcp_t server;
  адреса argument;
  БібліотекаМавкиІнетСлугаОбробникЗапуску on_start;
  БібліотекаМавкиІнетСлугаОбробникПомилки on_error;
  БібліотекаМавкиІнетСлугаОбробникЗупинки on_stop;
  БібліотекаМавкиІнетСлугаОбробникЗнищення on_destroy;
  БібліотекаМавкиІнетСлугаОбробникПідключенняЗвʼязку on_connection;
  БібліотекаМавкиІнетСлугаОбробникДанихЗвʼязку on_data;
  БібліотекаМавкиІнетСлугаОбробникСтіканняЗвʼязку on_drain;
  БібліотекаМавкиІнетСлугаОбробникЗакінченняЗвʼязку on_end;
  БібліотекаМавкиІнетСлугаОбробникПомилкиЗвʼязку on_conn_error;
  БібліотекаМавкиІнетСлугаОбробникВідключенняЗвʼязку on_disconnect;
  int closing;
  int server_handle_closed;
  int active_conns;
} MavkaServer;

typedef struct MavkaConn MavkaConn;

struct MavkaConn {
  uv_tcp_t handle;
  MavkaServer* server;
  адреса conn_argument;
  БібліотекаМавкиІнетЗвʼязокОбробникЗнищення on_conn_destroy;
  БібліотекаМавкиІнетКлієнтОбробникПідключення client_on_connect;
  БібліотекаМавкиІнетКлієнтОбробникДаних client_on_data;
  БібліотекаМавкиІнетКлієнтОбробникСтікання client_on_drain;
  БібліотекаМавкиІнетКлієнтОбробникЗакінчення client_on_end;
  БібліотекаМавкиІнетКлієнтОбробникПомилки client_on_error;
  БібліотекаМавкиІнетКлієнтОбробникВідключення client_on_disconnect;
  uv_connect_t connect_req;
  int is_client;
  int write_backlog_active;
  int pending_close;
  int end_disconnect;
  size_t active_writes;
  MavkaConnState state;
};

typedef struct {
  uv_write_t req;
  uv_buf_t buf;
  MavkaConn* conn;
  адреса arg;
  БібліотекаМавкиІнетЗвʼязокОбробникНадіслання on_send;
  БібліотекаМавкиІнетЗвʼязокОбробникЗакінчення on_end;
  int is_end_req;
} MavkaWriteReq;

static природне map_uv_error(int err) {
  switch (err) {
    case 0:
      return МАВКА_ІНЕТ_УСПІХ;
    case UV_ENOMEM:
      return МАВКА_ІНЕТ_ПОМИЛКА_НЕДОСТАТНЬО_ПАМʼЯТІ;
    case UV_EINVAL:
      return МАВКА_ІНЕТ_ПОМИЛКА_АРГУМЕНТУ;
    case UV_EADDRINUSE:
      return МАВКА_ІНЕТ_ПОМИЛКА_АДРЕСА_ВИКОРИСТОВУЄТЬСЯ;
    case UV_ECONNREFUSED:
      return МАВКА_ІНЕТ_ПОМИЛКА_ЗʼЄДНАННЯ_ВІДХИЛЕНО;
    case UV_ECONNRESET:
      return МАВКА_ІНЕТ_ПОМИЛКА_ЗʼЄДНАННЯ_СКИНОТО;
    case UV_ENETUNREACH:
      return МАВКА_ІНЕТ_ПОМИЛКА_МЕРЕЖА_НЕДОСТУПНА;
    case UV_EHOSTUNREACH:
    case UV_EHOSTDOWN:
      return МАВКА_ІНЕТ_ПОМИЛКА_ХОСТ_НЕДОСТУПНИЙ;
    case UV_ETIMEDOUT:
      return МАВКА_ІНЕТ_ПОМИЛКА_ТАЙМАУТ;
    case UV_EPIPE:
    case UV_ECANCELED:
    case UV_ESHUTDOWN:
      return МАВКА_ІНЕТ_ПОМИЛКА_КАНАЛ_ЗЛАМАНО;
    case UV_EACCES:
    case UV_EPERM:
      return МАВКА_ІНЕТ_ПОМИЛКА_ДОСТУПУ;
    case UV_EMFILE:
    case UV_ENFILE:
      return МАВКА_ІНЕТ_ПОМИЛКА_ЗАБАГАТО_ФАЙЛІВ;
    case UV_EADDRNOTAVAIL:
      return МАВКА_ІНЕТ_ПОМИЛКА_АДРЕСА_НЕДОСТУПНА;
    case UV_ENOBUFS:
      return МАВКА_ІНЕТ_ПОМИЛКА_ПЕРЕПОВНЕННЯ_БУФЕРА;
    case UV_ENETDOWN:
      return МАВКА_ІНЕТ_ПОМИЛКА_МЕРЕЖА_ВІДМОВИЛА;
    case UV_ECONNABORTED:
      return МАВКА_ІНЕТ_ПОМИЛКА_ЗʼЄДНАННЯ_ПЕРЕРВАНО;
    case UV_EISCONN:
      return МАВКА_ІНЕТ_ПОМИЛКА_ВЖЕ_ПІДДОСЛІДЖЕНО;
    case UV_ENOTCONN:
    case UV_ENOTSOCK:
      return МАВКА_ІНЕТ_ПОМИЛКА_НЕ_ПІДʼЄДНАНО;
    case UV_EIO:
      return МАВКА_ІНЕТ_ПОМИЛКА_ВХІД_ВИХІД;
    case UV_EBADF:
      return МАВКА_ІНЕТ_ПОМИЛКА_НЕПРАВИЛЬНИЙ_ФАЙЛ;
    default:
      return МАВКА_ІНЕТ_ПОМИЛКА_НЕВІДОМА;
  }
}

static void fire_disconnect_if_pending(MavkaConn* conn) {
  if (conn->end_disconnect) {
    conn->end_disconnect = 0;
    if (conn->server && conn->server->on_disconnect) {
      conn->server->on_disconnect((адреса)conn->server, (адреса)conn);
    } else if (conn->is_client && conn->client_on_disconnect) {
      conn->client_on_disconnect((адреса)conn);
    }
  }
}

static void alloc_cb(uv_handle_t* handle,
                     size_t suggested_size,
                     uv_buf_t* buf) {
  (void)handle;
  buf->base = (char*)пристрій_мавки_виділити(suggested_size);
  buf->len = buf->base ? suggested_size : 0;
}

static void maybe_destroy_server(MavkaServer* server) {
  if (server->server_handle_closed && server->active_conns == 0) {
    if (server->on_destroy) {
      server->on_destroy(server->argument);
    }
    пристрій_мавки_звільнити(server);
  }
}

static void conn_close_cb(uv_handle_t* handle) {
  MavkaConn* conn = (MavkaConn*)handle->data;
  conn->state = CONN_STATE_DISCONNECTED;

  fire_disconnect_if_pending(conn);

  MavkaServer* server = conn->server;
  if (server) {
    server->active_conns--;
  }

  if (conn->on_conn_destroy) {
    conn->on_conn_destroy(conn->conn_argument);
  }
  пристрій_мавки_звільнити(conn);

  if (server) {
    maybe_destroy_server(server);
  }
}

static void safe_close_conn(MavkaConn* conn) {
  if (conn->state == CONN_STATE_DISCONNECTED ||
      conn->state == CONN_STATE_CLOSING) {
    return;
  }

  conn->pending_close = 1;

  if (conn->active_writes == 0 && !uv_is_closing((uv_handle_t*)&conn->handle)) {
    conn->state = CONN_STATE_CLOSING;
    uv_close((uv_handle_t*)&conn->handle, conn_close_cb);
  }
}

static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
  MavkaConn* conn = (MavkaConn*)stream->data;

  if (nread > 0) {
    if (conn->server && conn->server->on_data) {
      conn->server->on_data((адреса)conn->server, (адреса)conn, (п8*)buf->base,
                            (природне)nread);
    } else if (conn->is_client && conn->client_on_data) {
      conn->client_on_data((адреса)conn, (п8*)buf->base, (природне)nread);
    } else {
      пристрій_мавки_звільнити(buf->base);
    }
  } else if (nread < 0) {
    uv_read_stop(stream);
    if (buf->base) {
      пристрій_мавки_звільнити(buf->base);
    }

    if (nread == UV_EOF) {
      if (conn->server && conn->server->on_end) {
        conn->server->on_end((адреса)conn->server, (адреса)conn);
      } else if (conn->is_client && conn->client_on_end) {
        conn->client_on_end((адреса)conn);
      }
    } else {
      природне err_code = map_uv_error((int)nread);
      if (conn->server && conn->server->on_conn_error) {
        conn->server->on_conn_error((адреса)conn->server, (адреса)conn,
                                    err_code);
      } else if (conn->is_client && conn->client_on_error) {
        conn->client_on_error((адреса)conn, err_code);
      }
    }

    if (conn->server && conn->server->on_disconnect) {
      conn->server->on_disconnect((адреса)conn->server, (адреса)conn);
    } else if (conn->is_client && conn->client_on_disconnect) {
      conn->client_on_disconnect((адреса)conn);
    }

    safe_close_conn(conn);
  }
}

static void server_close_cb(uv_handle_t* handle) {
  MavkaServer* server = (MavkaServer*)handle->data;
  server->server_handle_closed = 1;

  if (server->on_stop) {
    server->on_stop((адреса)server);
  }

  maybe_destroy_server(server);
}

static void on_new_connection(uv_stream_t* server_handle, int status) {
  MavkaServer* server = (MavkaServer*)server_handle->data;
  if (server->closing) {
    return;
  }

  if (status < 0) {
    if (server->on_error) {
      server->on_error((адреса)server, map_uv_error(status));
    }
    return;
  }

  MavkaConn* conn = (MavkaConn*)каллок(1, sizeof(MavkaConn));
  if (!conn) {
    return;
  }

  conn->is_client = 0;
  conn->state = CONN_STATE_CONNECTED;

  if (uv_tcp_init(uv_default_loop(), &conn->handle) != 0) {
    пристрій_мавки_звільнити(conn);
    return;
  }
  conn->handle.data = conn;

  if (uv_accept(server_handle, (uv_stream_t*)&conn->handle) == 0) {
    conn->server = server;
    server->active_conns++;

    if (server->on_connection) {
      server->on_connection((адреса)server, (адреса)conn);
    }

    if (uv_read_start((uv_stream_t*)&conn->handle, alloc_cb, read_cb) != 0) {
      safe_close_conn(conn);
    }
  } else {
    conn->state = CONN_STATE_CLOSING;
    uv_close((uv_handle_t*)&conn->handle, conn_close_cb);
  }
}

static int build_addr(природне іа_вид,
                      п8* іа_дані,
                      природне іа_розмір,
                      природне порт,
                      struct sockaddr_storage* addr) {
  мемсет(addr, 0, sizeof(*addr));

  if (іа_вид == 4) {
    char ip_str[64];
    if (іа_розмір >= sizeof(ip_str)) {
      return -1;
    }
    мемкоп(ip_str, іа_дані, іа_розмір);
    ip_str[іа_розмір] = '\0';
    return uv_ip4_addr(ip_str, (int)порт, (struct sockaddr_in*)addr);
  } else if (іа_вид == 6) {
    if (іа_розмір != 16) {
      return -1;
    }
    struct sockaddr_in6* addr6 = (struct sockaddr_in6*)addr;
    addr6->sin6_family = AF_INET6;
    addr6->sin6_port = htons((uint16_t)порт);
    мемкоп(&addr6->sin6_addr, іа_дані, 16);
    return 0;
  }

  return -1;
}

void бібліотека_мавки_інет_служити(
    природне іа_вид,
    п8* іа_дані,
    природне іа_розмір,
    природне порт,
    БібліотекаМавкиІнетСлугаОбробникЗапуску обробник_запуску,
    БібліотекаМавкиІнетСлугаОбробникПомилки обробник_помилки,
    БібліотекаМавкиІнетСлугаОбробникЗупинки обробник_зупинки,
    адреса аргумент,
    БібліотекаМавкиІнетСлугаОбробникЗнищення обробник_знищення,
    БібліотекаМавкиІнетСлугаОбробникПідключенняЗвʼязку
        обробник_підключення_звʼязку,
    БібліотекаМавкиІнетСлугаОбробникДанихЗвʼязку обробник_даних_звʼязку,
    БібліотекаМавкиІнетСлугаОбробникСтіканняЗвʼязку обробник_стікання_звʼязку,
    БібліотекаМавкиІнетСлугаОбробникЗакінченняЗвʼязку
        обробник_закінчення_звʼязку,
    БібліотекаМавкиІнетСлугаОбробникПомилкиЗвʼязку обробник_помилки_звʼязку,
    БібліотекаМавкиІнетСлугаОбробникВідключенняЗвʼязку
        обробник_відключення_звʼязку) {
  MavkaServer* server = (MavkaServer*)каллок(1, sizeof(MavkaServer));
  if (!server) {
    return;
  }

  server->argument = аргумент;
  server->on_start = обробник_запуску;
  server->on_error = обробник_помилки;
  server->on_stop = обробник_зупинки;
  server->on_destroy = обробник_знищення;
  server->on_connection = обробник_підключення_звʼязку;
  server->on_data = обробник_даних_звʼязку;
  server->on_drain = обробник_стікання_звʼязку;
  server->on_end = обробник_закінчення_звʼязку;
  server->on_conn_error = обробник_помилки_звʼязку;
  server->on_disconnect = обробник_відключення_звʼязку;

  if (uv_tcp_init(uv_default_loop(), &server->server) != 0) {
    server->closing = 1;
    server->server_handle_closed = 1;
    if (server->on_start) {
      server->on_start((адреса)server, МАВКА_ІНЕТ_ПОМИЛКА_НЕВІДОМА);
    }
    if (server->on_destroy) {
      server->on_destroy(server->argument);
    }
    пристрій_мавки_звільнити(server);
    return;
  }

  server->server.data = server;

  struct sockaddr_storage addr;
  int addr_res = build_addr(іа_вид, іа_дані, іа_розмір, порт, &addr);
  if (addr_res != 0) {
    природне err = (addr_res == -1) ? МАВКА_ІНЕТ_ПОМИЛКА_АРГУМЕНТУ
                                    : map_uv_error(addr_res);
    if (server->on_start) {
      server->on_start((адреса)server, err);
    }
    uv_close((uv_handle_t*)&server->server, server_close_cb);
    return;
  }

  int res = uv_tcp_bind(&server->server, (const struct sockaddr*)&addr, 0);
  if (res != 0) {
    if (server->on_start) {
      server->on_start((адреса)server, map_uv_error(res));
    }
    uv_close((uv_handle_t*)&server->server, server_close_cb);
    return;
  }

  res = uv_listen((uv_stream_t*)&server->server, SOMAXCONN, on_new_connection);
  if (res != 0) {
    if (server->on_start) {
      server->on_start((адреса)server, map_uv_error(res));
    }
    uv_close((uv_handle_t*)&server->server, server_close_cb);
    return;
  }

  if (server->on_start) {
    server->on_start((адреса)server, МАВКА_ІНЕТ_УСПІХ);
  }
}

адреса бібліотека_мавки_інет_отримати_аргумент_слуги(адреса адр_слуга) {
  return ((MavkaServer*)адр_слуга)->argument;
}

static void close_walk_cb(uv_handle_t* handle, void* arg) {
  MavkaServer* server = (MavkaServer*)arg;

  if (uv_is_closing(handle)) {
    return;
  }

  if (handle == (uv_handle_t*)&server->server) {
    uv_close(handle, server_close_cb);
    return;
  }

  if (handle->type == UV_TCP) {
    MavkaConn* conn = (MavkaConn*)handle->data;
    if (conn && !conn->is_client && conn->server == server) {
      conn->state = CONN_STATE_CLOSING;
      uv_close(handle, conn_close_cb);
    }
  }
}

void бібліотека_мавки_інет_зупинити_слугу(адреса адр_слуга,
                                          природне закрити_підключення) {
  MavkaServer* server = (MavkaServer*)адр_слуга;
  if (server->closing) {
    return;
  }

  server->closing = 1;

  if (закрити_підключення) {
    uv_walk(uv_default_loop(), close_walk_cb, server);
  } else if (!uv_is_closing((uv_handle_t*)&server->server)) {
    uv_close((uv_handle_t*)&server->server, server_close_cb);
  }
}

static void on_connect(uv_connect_t* req, int status) {
  MavkaConn* conn = (MavkaConn*)req->data;

  природне err_code = map_uv_error(status);

  if (status == 0) {
    conn->state = CONN_STATE_CONNECTED;
  }

  if (conn->client_on_connect) {
    conn->client_on_connect((адреса)conn, err_code);
  }

  if (status < 0) {
    safe_close_conn(conn);
    return;
  }

  if (uv_read_start((uv_stream_t*)&conn->handle, alloc_cb, read_cb) != 0) {
    safe_close_conn(conn);
  }
}

void бібліотека_мавки_інет_підключитись(
    природне іа_вид,
    п8* іа_дані,
    природне іа_розмір,
    природне порт,
    БібліотекаМавкиІнетКлієнтОбробникПідключення обробник_підключення,
    БібліотекаМавкиІнетКлієнтОбробникДаних обробник_даних,
    БібліотекаМавкиІнетКлієнтОбробникСтікання обробник_стікання,
    БібліотекаМавкиІнетКлієнтОбробникЗакінчення обробник_закінчення,
    БібліотекаМавкиІнетКлієнтОбробникПомилки обробник_помилки,
    БібліотекаМавкиІнетКлієнтОбробникВідключення обробник_відключення,
    адреса аргумент,
    БібліотекаМавкиІнетЗвʼязокОбробникЗнищення обробник_знищення) {
  MavkaConn* conn = (MavkaConn*)каллок(1, sizeof(MavkaConn));
  if (!conn) {
    return;
  }

  conn->is_client = 1;
  conn->state = CONN_STATE_CONNECTING;
  conn->client_on_connect = обробник_підключення;
  conn->client_on_data = обробник_даних;
  conn->client_on_drain = обробник_стікання;
  conn->client_on_end = обробник_закінчення;
  conn->client_on_error = обробник_помилки;
  conn->client_on_disconnect = обробник_відключення;
  conn->conn_argument = аргумент;
  conn->on_conn_destroy = обробник_знищення;

  if (uv_tcp_init(uv_default_loop(), &conn->handle) != 0) {
    conn->state = CONN_STATE_DISCONNECTED;
    if (conn->client_on_connect) {
      conn->client_on_connect((адреса)conn, МАВКА_ІНЕТ_ПОМИЛКА_НЕВІДОМА);
    }
    if (conn->on_conn_destroy) {
      conn->on_conn_destroy(conn->conn_argument);
    }
    пристрій_мавки_звільнити(conn);
    return;
  }

  conn->handle.data = conn;
  conn->connect_req.data = conn;

  struct sockaddr_storage addr;
  int addr_res = build_addr(іа_вид, іа_дані, іа_розмір, порт, &addr);
  if (addr_res != 0) {
    природне err = (addr_res == -1) ? МАВКА_ІНЕТ_ПОМИЛКА_АРГУМЕНТУ
                                    : map_uv_error(addr_res);
    if (conn->client_on_connect) {
      conn->client_on_connect((адреса)conn, err);
    }
    safe_close_conn(conn);
    return;
  }

  int res = uv_tcp_connect(&conn->connect_req, &conn->handle,
                           (const struct sockaddr*)&addr, on_connect);
  if (res != 0) {
    if (conn->client_on_connect) {
      conn->client_on_connect((адреса)conn, map_uv_error(res));
    }
    safe_close_conn(conn);
    return;
  }
}

природне бібліотека_мавки_інет_отримати_іа_звʼязку(адреса адр_звʼязок,
                                                   природне* вихід_іа4,
                                                   п8* вихід_іа6) {
  MavkaConn* conn = (MavkaConn*)адр_звʼязок;

  struct sockaddr_storage name;
  int namelen = sizeof(name);

  if (uv_tcp_getsockname(&conn->handle, (struct sockaddr*)&name, &namelen) !=
      0) {
    return 0;
  }

  if (name.ss_family == AF_INET) {
    if (вихід_іа4) {
      struct sockaddr_in* s4 = (struct sockaddr_in*)&name;
      *вихід_іа4 = (природне)ntohl(s4->sin_addr.s_addr);
    }
    return 4;
  } else if (name.ss_family == AF_INET6) {
    if (вихід_іа6) {
      struct sockaddr_in6* s6 = (struct sockaddr_in6*)&name;
      мемкоп(вихід_іа6, s6->sin6_addr.s6_addr, 16);
    }
    return 6;
  }

  return 0;
}

void бібліотека_мавки_інет_записати_аргумент_звʼязку(
    адреса адр_звʼязок,
    адреса аргумент,
    БібліотекаМавкиІнетЗвʼязокОбробникЗнищення обробник_знищення) {
  MavkaConn* conn = (MavkaConn*)адр_звʼязок;
  conn->conn_argument = аргумент;
  conn->on_conn_destroy = обробник_знищення;
}

адреса бібліотека_мавки_інет_отримати_аргумент_звʼязку(адреса адр_звʼязок) {
  return ((MavkaConn*)адр_звʼязок)->conn_argument;
}

static void on_write_complete(uv_write_t* req, int status) {
  MavkaWriteReq* wreq = (MavkaWriteReq*)req->data;
  MavkaConn* conn = wreq->conn;

  conn->active_writes--;

  if (wreq->is_end_req) {
    conn->pending_close = 1;
  }

  if (status == 0) {
    if (wreq->on_send) {
      wreq->on_send((адреса)conn, wreq->arg);
    }
    if (wreq->on_end) {
      wreq->on_end((адреса)conn, wreq->arg);
    }

    if (!conn->pending_close && conn->write_backlog_active) {
      size_t write_queue_size =
          uv_stream_get_write_queue_size((uv_stream_t*)&conn->handle);
      if (write_queue_size < MAVKA_NET_HIGH_WATERMARK) {
        conn->write_backlog_active = 0;
        if (conn->server && conn->server->on_drain) {
          conn->server->on_drain((адреса)conn->server, (адреса)conn);
        } else if (conn->is_client && conn->client_on_drain) {
          conn->client_on_drain((адреса)conn);
        }
      }
    }
  } else {
    conn->write_backlog_active = 0;
    природне err_code = map_uv_error(status);
    if (conn->server && conn->server->on_conn_error) {
      conn->server->on_conn_error((адреса)conn->server, (адреса)conn, err_code);
    } else if (conn->is_client && conn->client_on_error) {
      conn->client_on_error((адреса)conn, err_code);
    }
    conn->pending_close = 1;
  }

  if (conn->pending_close && conn->active_writes == 0 &&
      conn->state != CONN_STATE_CLOSING &&
      conn->state != CONN_STATE_DISCONNECTED &&
      !uv_is_closing((uv_handle_t*)&conn->handle)) {
    fire_disconnect_if_pending(conn);
    conn->state = CONN_STATE_CLOSING;
    uv_close((uv_handle_t*)&conn->handle, conn_close_cb);
  }

  if (wreq->buf.base) {
    пристрій_мавки_звільнити(wreq->buf.base);
  }
  пристрій_мавки_звільнити(wreq);
}

логічне бібліотека_мавки_інет_звʼязок_надіслати(
    адреса адр_звʼязок,
    п8* дані,
    природне розмір,
    адреса аргумент,
    БібліотекаМавкиІнетЗвʼязокОбробникНадіслання обробник) {
  MavkaConn* conn = (MavkaConn*)адр_звʼязок;

  if (conn->state != CONN_STATE_CONNECTED || conn->pending_close) {
    return 0;
  }

  if (розмір == 0) {
    if (обробник) {
      обробник((адреса)conn, аргумент);
    }
    return 1;
  }

  if (!дані) {
    return 0;
  }

  MavkaWriteReq* wreq = (MavkaWriteReq*)каллок(1, sizeof(MavkaWriteReq));
  if (!wreq) {
    return 0;
  }

  char* data_copy = (char*)пристрій_мавки_виділити((size_t)розмір);
  if (!data_copy) {
    пристрій_мавки_звільнити(wreq);
    return 0;
  }

  мемкоп(data_copy, дані, (size_t)розмір);
  wreq->buf = uv_buf_init(data_copy, (unsigned int)розмір);
  wreq->conn = conn;
  wreq->arg = аргумент;
  wreq->on_send = обробник;
  wreq->is_end_req = 0;
  wreq->req.data = wreq;

  conn->active_writes++;

  int res = uv_write(&wreq->req, (uv_stream_t*)&conn->handle, &wreq->buf, 1,
                     on_write_complete);
  if (res != 0) {
    conn->active_writes--;
    пристрій_мавки_звільнити(data_copy);
    пристрій_мавки_звільнити(wreq);
    return 0;
  }

  if (uv_stream_get_write_queue_size((uv_stream_t*)&conn->handle) >=
      MAVKA_NET_HIGH_WATERMARK) {
    conn->write_backlog_active = 1;
  }

  return 1;
}

void бібліотека_мавки_інет_звʼязок_закінчити(
    адреса адр_звʼязок,
    п8* дані,
    природне розмір,
    адреса аргумент,
    БібліотекаМавкиІнетЗвʼязокОбробникЗакінчення обробник) {
  MavkaConn* conn = (MavkaConn*)адр_звʼязок;

  if (conn->state != CONN_STATE_CONNECTED || conn->pending_close) {
    if (обробник) {
      обробник((адреса)conn, аргумент);
    }
    return;
  }

  conn->end_disconnect = 1;

  if (дані && розмір > 0) {
    MavkaWriteReq* wreq = (MavkaWriteReq*)каллок(1, sizeof(MavkaWriteReq));
    if (wreq) {
      char* data_copy = (char*)пристрій_мавки_виділити((size_t)розмір);
      if (data_copy) {
        мемкоп(data_copy, дані, (size_t)розмір);
        wreq->buf = uv_buf_init(data_copy, (unsigned int)розмір);
        wreq->conn = conn;
        wreq->arg = аргумент;
        wreq->on_end = обробник;
        wreq->is_end_req = 1;
        wreq->req.data = wreq;

        conn->active_writes++;

        if (uv_write(&wreq->req, (uv_stream_t*)&conn->handle, &wreq->buf, 1,
                     on_write_complete) == 0) {
          if (uv_stream_get_write_queue_size((uv_stream_t*)&conn->handle) >=
              MAVKA_NET_HIGH_WATERMARK) {
            conn->write_backlog_active = 1;
          }
          return;
        }

        conn->active_writes--;
        пристрій_мавки_звільнити(data_copy);
      }
      пристрій_мавки_звільнити(wreq);
    }
  }

  if (обробник) {
    обробник((адреса)conn, аргумент);
  }
  fire_disconnect_if_pending(conn);
  safe_close_conn(conn);
}

void бібліотека_мавки_інет_звʼязок_знищити(адреса адр_звʼязок) {
  MavkaConn* conn = (MavkaConn*)адр_звʼязок;
  safe_close_conn(conn);
}