#include "./include/mavka/prystriy.h"
#include "./include/mavka/tsil.h"

static char* MAVKA_VERSION_STATIC = MAVKA_VERSION;

bool pristriy_mavky_get_version(char** data_out, size_t* size_out) {
  *data_out = MAVKA_VERSION_STATIC;
  *size_out = 0;

  while ((*data_out)[*size_out] != '\0') {
    *size_out = (*size_out) + 1;
  }

  return 1;
}

логічне пристрій_мавки_перекодувати_ю8_в_кд(природне розмір,
                                            п8* дані,
                                            природне* вихід_розміру,
                                            п8** вихід_даних,
                                            природне* вихід_позиції_помилки);

логічне пристрій_мавки_перекодувати_кд_в_ю8(природне розмір,
                                            п8* дані,
                                            природне* вихід_розміру,
                                            п8** вихід_даних,
                                            природне* вихід_позиції_помилки);

логічне пристрій_мавки_отримати_версію(п8** вихід_даних,
                                       природне* вихід_розміру) {
  п8* дані_версії_ю8;
  природне довжина_версії_ю8;

  if (!pristriy_mavky_get_version((char**)&дані_версії_ю8,
                                  (size_t*)&довжина_версії_ю8)) {
    return ні;
  }

  if (!пристрій_мавки_перекодувати_ю8_в_кд(довжина_версії_ю8, дані_версії_ю8,
                                           вихід_розміру, вихід_даних, пусто)) {
    return ні;
  }

  return так;
}

п8 пристрій_мавки_отримати_ос() {
#if defined(_WIN32) || defined(_MSC_VER) || defined(__WIN32__)
  return 18;
#elif defined(__linux__)
  return 17;
#elif defined(__APPLE__)
  return 24;
#elif defined(__FreeBSD__)
  return 58;
#endif

  return 0;
}

адреса пристрій_мавки_виділити(природне розмір) {
  return (адреса)pristriy_mavky_malloc((size_t)розмір);
}

адреса пристрій_мавки_перевиділити(адреса значення, природне новий_розмір) {
  return (адреса)pristriy_mavky_realloc((void*)значення, (size_t)новий_розмір);
}

void пристрій_мавки_звільнити(адреса значення) {
  pristriy_mavky_free((void*)значення);
}

void пристрій_мавки_вивести_сирі_дані(п8* дані, природне розмір) {
  pristriy_mavky_write_to_stdout((char*)дані, (size_t)розмір);
}

void пристрій_мавки_вивести_ю8(природне колір, п8* дані, природне розмір) {
  pristriy_mavky_write_utf8_to_stdout((PristriyMavkyWriteToStdoutColor)колір,
                                      (char*)дані, (size_t)розмір);
}

void пристрій_мавки_вивести_кд(природне колір, п8* дані, природне розмір) {
  природне розмір_ю8;
  п8* дані_ю8;

  if (!пристрій_мавки_перекодувати_кд_в_ю8(розмір, дані, &розмір_ю8, &дані_ю8,
                                           пусто)) {
    // потім: обробити
    return;
  }

  пристрій_мавки_вивести_ю8(колір, дані_ю8, розмір_ю8);

  пристрій_мавки_звільнити(дані_ю8);
}

р64 пристрій_мавки_мат_степінь(р64 значення, р64 степінь) {
  return (double)pristriy_mavky_double_pow((double)значення, (double)степінь);
}

р64 пристрій_мавки_мат_син(р64 значення) {
  return (double)pristriy_mavky_double_sin((double)значення);
}

р64 пристрій_мавки_мат_кос(р64 значення) {
  return (double)pristriy_mavky_double_cos((double)значення);
}

р64 пристрій_мавки_мат_тан(р64 значення) {
  return (double)pristriy_mavky_double_tan((double)значення);
}

р64 пристрій_мавки_мат_асин(р64 значення) {
  return (double)pristriy_mavky_double_asin((double)значення);
}

р64 пристрій_мавки_мат_акос(р64 значення) {
  return (double)pristriy_mavky_double_acos((double)значення);
}

р64 пристрій_мавки_мат_атан(р64 значення) {
  return (double)pristriy_mavky_double_atan((double)значення);
}

р64 пристрій_мавки_мат_абс(р64 значення) {
  return (double)pristriy_mavky_double_abs((double)значення);
}

р64 пристрій_мавки_мат_експ(р64 значення) {
  return (double)pristriy_mavky_double_exp((double)значення);
}

р64 пристрій_мавки_мат_кор2(р64 значення) {
  return (double)pristriy_mavky_double_sqrt((double)значення);
}

р64 пристрій_мавки_мат_стеля(р64 значення) {
  return (double)pristriy_mavky_double_ceil((double)значення);
}

р64 пристрій_мавки_мат_підлога(р64 значення) {
  return (double)pristriy_mavky_double_floor((double)значення);
}

р64 пристрій_мавки_мат_округлити(р64 значення) {
  return (double)pristriy_mavky_double_round((double)значення);
}

логічне пристрій_мавки_перетворити_п64_в_кд(п64 значення,
                                            п8** вихід_даних,
                                            природне* вихід_розміру) {
  природне розмір_ю8;
  п8* дані_ю8;
  природне позиція_помилки;

  if (!pristriy_mavky_uint64_to_str((size_t)значення, (char**)&дані_ю8,
                                    (size_t*)&розмір_ю8)) {
    return ні;
  }

  if (!пристрій_мавки_перекодувати_ю8_в_кд(розмір_ю8, дані_ю8, вихід_розміру,
                                           вихід_даних, &позиція_помилки)) {
    пристрій_мавки_звільнити(дані_ю8);
    return ні;
  }

  пристрій_мавки_звільнити(дані_ю8);
  return так;
}

логічне пристрій_мавки_перетворити_ц64_в_кд(ц64 значення,
                                            п8** вихід_даних,
                                            природне* вихід_розміру) {
  природне розмір_ю8;
  п8* дані_ю8;
  природне позиція_помилки;

  if (!pristriy_mavky_int64_to_str((ssize_t)значення, (char**)&дані_ю8,
                                   (size_t*)&розмір_ю8)) {
    return ні;
  }

  if (!пристрій_мавки_перекодувати_ю8_в_кд(розмір_ю8, дані_ю8, вихід_розміру,
                                           вихід_даних, &позиція_помилки)) {
    пристрій_мавки_звільнити(дані_ю8);
    return ні;
  }

  пристрій_мавки_звільнити(дані_ю8);
  return так;
}

логічне пристрій_мавки_перетворити_р64_в_кд_з_експонентою(
    р64 значення,
    п8** вихід_даних,
    природне* вихід_розміру,
    ціле* вихід_розміру_експоненти) {
  природне розмір_ю8;
  п8* дані_ю8;
  природне позиція_помилки;

  if (!pristriy_mavky_double_to_str((double)значення, (char**)&дані_ю8,
                                    (size_t*)&розмір_ю8,
                                    (ssize_t*)вихід_розміру_експоненти)) {
    return ні;
  }

  if (!пристрій_мавки_перекодувати_ю8_в_кд(розмір_ю8, дані_ю8, вихід_розміру,
                                           вихід_даних, &позиція_помилки)) {
    пристрій_мавки_звільнити(дані_ю8);
    return ні;
  }

  пристрій_мавки_звільнити(дані_ю8);
  return так;
}

typedef struct НакопичувачТексту {
  п8* дані;
  природне розмір;
  природне вмісткість;
} НакопичувачТексту;

static НакопичувачТексту зробити_накопичувач_тексту(природне вмісткість) {
  return (НакопичувачТексту){(п8*)пристрій_мавки_виділити(вмісткість), 0,
                             вмісткість};
}

static void покласти_в_накопичувач_тексту(НакопичувачТексту* нт, кд значення) {
  природне новий_розмір = нт->розмір + значення.розмір;

  нт->дані = (п8*)пристрій_мавки_перевиділити(нт->дані, новий_розмір);

  природне п = 0;
  while (п < значення.розмір) {
    нт->дані[нт->розмір + п] = значення.дані[п];
    п += 1;
  }

  нт->розмір = новий_розмір;
}

логічне пристрій_мавки_перетворити_р64_в_кд(р64 значення,
                                            п8** вихід_даних,
                                            природне* вихід_розміру) {
  кд значення_кд;
  ціле розмір_експоненти = 0;
  п8 е[1] = {68};
  п8 плюс[1] = {26};
  кд кд_е = {е, 1};
  кд кд_плюс = {плюс, 1};

  if (пристрій_мавки_перетворити_р64_в_кд_з_експонентою(
          значення, &значення_кд.дані, &значення_кд.розмір,
          &розмір_експоненти)) {
    if (розмір_експоненти == 0) {
      *вихід_розміру = значення_кд.розмір;
      *вихід_даних = значення_кд.дані;

      return так;
    }

    НакопичувачТексту накопичувач_тексту =
        зробити_накопичувач_тексту(значення_кд.розмір + 10);
    покласти_в_накопичувач_тексту(&накопичувач_тексту, значення_кд);

    кд значення_експоненти_кд;

    if (пристрій_мавки_перетворити_ц64_в_кд(розмір_експоненти,
                                            &значення_експоненти_кд.дані,
                                            &значення_експоненти_кд.розмір)) {
      покласти_в_накопичувач_тексту(&накопичувач_тексту, кд_е);

      if (розмір_експоненти > 0) {
        покласти_в_накопичувач_тексту(&накопичувач_тексту, кд_плюс);
      }

      покласти_в_накопичувач_тексту(&накопичувач_тексту,
                                    значення_експоненти_кд);

      пристрій_мавки_звільнити(значення_експоненти_кд.дані);
      пристрій_мавки_звільнити(значення_кд.дані);

      *вихід_розміру = накопичувач_тексту.розмір;
      *вихід_даних = накопичувач_тексту.дані;

      return так;
    }

    пристрій_мавки_звільнити(накопичувач_тексту.дані);
    пристрій_мавки_звільнити(значення_кд.дані);

    return так;
  }

  return ні;
}

логічне пристрій_мавки_отримати_р64_з_кд(п8* дані,
                                         природне розмір,
                                         р64* вихід) {
  природне розмір_ю8;
  п8* дані_ю8;
  природне позиція_помилки;

  if (!пристрій_мавки_перекодувати_кд_в_ю8(розмір, дані, &розмір_ю8, &дані_ю8,
                                           &позиція_помилки)) {
    return ні;
  }

  if (!pristriy_mavky_str_to_double((char*)дані_ю8, (size_t)розмір_ю8,
                                    (double*)вихід)) {
    пристрій_мавки_звільнити(дані_ю8);
    return ні;
  }

  пристрій_мавки_звільнити(дані_ю8);
  return так;
}

логічне пристрій_мавки_отримати_поточний_шлях_процесу(п8** вихід_даних,
                                                      природне* вихід_розміру) {
  п8* дані_шляху_ю8;
  природне розмір_шляху_ю8;
  логічне успіх;

  успіх =
      pristriy_mavky_get_cwd((char**)&дані_шляху_ю8, (size_t*)&розмір_шляху_ю8);
  if (!успіх) {
    return ні;
  }

  успіх = пристрій_мавки_перекодувати_ю8_в_кд(
      розмір_шляху_ю8, дані_шляху_ю8, вихід_розміру, вихід_даних, пусто);
  пристрій_мавки_звільнити(дані_шляху_ю8);
  return успіх;
}

логічне пристрій_мавки_перевірити_чи_шлях_існує_і_є_файлом(
    п8* дані_шляху,
    природне розмір_шляху) {
  п8* дані_шляху_ю8;
  природне розмір_шляху_ю8;
  логічне успіх;

  успіх = пристрій_мавки_перекодувати_кд_в_ю8(
      розмір_шляху, дані_шляху, &розмір_шляху_ю8, &дані_шляху_ю8, пусто);
  if (!успіх) {
    return ні;
  }

  успіх = pristriy_mavky_check_if_file_exists((char*)дані_шляху_ю8,
                                              (size_t)розмір_шляху_ю8);

  пристрій_мавки_звільнити(дані_шляху_ю8);

  return успіх;
}

логічне пристрій_мавки_отримати_абсолютний_шлях(п8* дані_шляху,
                                                природне розмір_шляху,
                                                п8** вихід_даних,
                                                природне* вихід_розміру) {
  п8* дані_шляху_ю8;
  природне розмір_шляху_ю8;
  п8* дані_нового_шляху_ю8;
  природне розмір_нового_шляху_ю8;
  логічне успіх;

  успіх = пристрій_мавки_перекодувати_кд_в_ю8(
      розмір_шляху, дані_шляху, &розмір_шляху_ю8, &дані_шляху_ю8, пусто);
  if (!успіх) {
    return ні;
  }

  успіх = pristriy_mavky_get_absolute_path(
      (char*)дані_шляху_ю8, (size_t)розмір_шляху_ю8,
      (char**)&дані_нового_шляху_ю8, (size_t*)&розмір_нового_шляху_ю8);
  пристрій_мавки_звільнити(дані_шляху_ю8);
  if (!успіх) {
    return ні;
  }

  успіх = пристрій_мавки_перекодувати_ю8_в_кд(
      розмір_нового_шляху_ю8, дані_нового_шляху_ю8, вихід_розміру, вихід_даних,
      пусто);
  пристрій_мавки_звільнити(дані_нового_шляху_ю8);
  return успіх;
}

логічне пристрій_мавки_прочитати_файл(п8* дані_шляху,
                                      природне розмір_шляху,
                                      п8** вихід_даних,
                                      природне* вихід_розміру) {
  п8* дані_шляху_до_файл_ю8;
  природне розмір_шляху_до_файлу_ю8;
  логічне успіх;

  успіх = пристрій_мавки_перекодувати_кд_в_ю8(розмір_шляху, дані_шляху,
                                              &розмір_шляху_до_файлу_ю8,
                                              &дані_шляху_до_файл_ю8, пусто);
  if (!успіх) {
    return ні;
  }

  успіх = pristriy_mavky_read_file((char*)дані_шляху_до_файл_ю8,
                                   (size_t)розмір_шляху_до_файлу_ю8,
                                   (char**)вихід_даних, (size_t*)вихід_розміру);
  пристрій_мавки_звільнити(дані_шляху_до_файл_ю8);
  return успіх;
}

typedef struct АргументЧитання {
  п8* дані_перед_ю8;
  void (*обробник)(п8* дані,
                   природне розмір,
                   логічне кінець_файлу,
                   адреса аргумент);
  адреса аргумент;
} АргументЧитання;

static void read_input_async_callback(char* data,
                                      size_t data_size,
                                      bool eof,
                                      void* arg) {
  АргументЧитання* ач = (АргументЧитання*)arg;
  логічне успіх;
  п8* дані_кд;
  природне розмір_кд;

  if (eof) {
    ач->обробник(пусто, 0, так, ач->аргумент);
  } else {
    успіх = пристрій_мавки_перекодувати_ю8_в_кд((природне)data_size, (п8*)data,
                                                &розмір_кд, &дані_кд, пусто);
    if (успіх) {
      ач->обробник(дані_кд, розмір_кд, ні, ач->аргумент);
    } else {
      ач->обробник(пусто, 0, ні, ач->аргумент);
    }
  }

  // пристрій_мавки_звільнити((void*)data);
  пристрій_мавки_звільнити(ач->дані_перед_ю8);
  пристрій_мавки_звільнити(ач);
}

логічне пристрій_мавки_читати_асинхронно(п8* дані_перед,
                                         природне розмір_перед,
                                         void (*обробник)(п8* дані,
                                                          природне розмір,
                                                          логічне кінець_файлу,
                                                          адреса аргумент),
                                         адреса аргумент,
                                         логічне дозволити_історію) {
  логічне успіх;
  п8* дані_перед_ю8;
  природне розмір_перед_ю8;

  успіх = пристрій_мавки_перекодувати_кд_в_ю8(
      розмір_перед, дані_перед, &розмір_перед_ю8, &дані_перед_ю8, пусто);
  if (!успіх) {
    return false;
  }

  АргументЧитання* ач = пристрій_мавки_виділити(sizeof(АргументЧитання));
  ач->дані_перед_ю8 = дані_перед_ю8;
  ач->обробник = обробник;
  ач->аргумент = аргумент;

  успіх = pristriy_mavky_read_input_async(
      (char*)дані_перед_ю8, (size_t)розмір_перед_ю8, read_input_async_callback,
      (void*)ач, (bool)дозволити_історію);
  if (!успіх) {
    пристрій_мавки_звільнити(дані_перед_ю8);
    пристрій_мавки_звільнити(ач);
  }

  return успіх;
}

адреса пристрій_мавки_відкрити_динамічну_бібліотеку(п8* дані_шляху,
                                                    природне розмір_шляху) {
  п8* дані_шляху_ю8;
  природне розмір_шляху_ю8;
  логічне успіх;
  int err_out;

  успіх = пристрій_мавки_перекодувати_кд_в_ю8(
      розмір_шляху, дані_шляху, &розмір_шляху_ю8, &дані_шляху_ю8, пусто);
  if (!успіх) {
    return NULL;
  }

  void* dl = pristriy_mavky_dlopen((char*)дані_шляху_ю8,
                                   (size_t)розмір_шляху_ю8, &err_out);

  пристрій_мавки_звільнити(дані_шляху_ю8);

  return dl;
}

адреса пристрій_мавки_отримати_символ_динамічної_бібліотеки(
    адреса дб,
    п8* дані_назви_символа,
    природне розмір_назви_символа) {
  п8* дані_назви_символа_ю8;
  природне розмір_назви_символа_ю8;
  логічне успіх;
  int err_out;

  успіх = пристрій_мавки_перекодувати_кд_в_ю8(
      розмір_назви_символа, дані_назви_символа, &розмір_назви_символа_ю8,
      &дані_назви_символа_ю8, пусто);
  if (!успіх) {
    return NULL;
  }

  void* dls = pristriy_mavky_dlsym((void*)дб, (char*)дані_назви_символа_ю8,
                                   (природне)розмір_назви_символа_ю8, &err_out);

  пристрій_мавки_звільнити(дані_назви_символа_ю8);

  return dls;
}

ціле пристрій_мавки_закрити_динамічну_бібліотеку(адреса дб) {
  return (ціле)pristriy_mavky_dlclose((void*)дб);
}

void пристрій_мавки_вийти(ціле код_виходу) {
  pristriy_mavky_exit((int)код_виходу);
}