// Воспроизведение MAP_FULL и решение через рост геометрии
// Раздел учебника: Том III, глава 17, «Рост, сжатие и дефрагментация», §17.3 «MAP_FULL».
// Что демонстрирует: малая геометрия + большой объём → MDBX_MAP_FULL;
//   разрешение через увеличение верхней границы (set_geometry) и повтор.
//   На 32-битной Windows переотображение на месте может быть недоступно
//   (MDBX_UNABLE_EXTEND_MAPSIZE); пример тогда переоткрывает БД с увеличенной
//   геометрией — результат тот же, но новое отображение создаётся сразу с нужным upper.
// Сборка: g++ -std=c++17 -I<libmdbx-include> -I../common 23-map-full.c++ -lmdbx
// Запуск: ./23-map-full
// Ожидаемый вывод:
//   MDBX_MAP_FULL after <N> inserts
//   grew geometry, continued: total <M> inserts
//   ok: MAP_FULL reproduced and resolved
// При сбое — сообщение в stderr и exit 1.
//
// SPDX-License-Identifier: Apache-2.0

#include <iostream>
#include <string>

#include "common.h++"
#include <mdbx.h++>

int main() {
  try {
    const std::string path = std::string(example::tmpdir()) + "/mdbx-23-map-full.mdbx";
    mdbx::env::remove(path);

    // Жёсткая геометрия: верхняя граница == текущий размер, всего 512 КБ.
    mdbx::env::geometry geo;
    geo.size_lower = 512 * 1024;
    geo.size_now = 512 * 1024;
    geo.size_upper = 512 * 1024;

    mdbx::env_managed env(path, mdbx::env_managed::create_parameters().set_geometry(geo),
                          mdbx::env::operate_parameters());

    size_t inserted = 0;
    try {
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      for (;;) {
        const auto key = "k" + std::to_string(inserted);
        const std::string value(1024, 'x');
        txn.insert(table, mdbx::slice(key), mdbx::slice(value));
        ++inserted;
      }
    } catch (const mdbx::db_full &) {
      std::cout << "MDBX_MAP_FULL after " << inserted << " inserts\n";
    }

    // Увеличиваем верхнюю границу — и продолжаем с того же места.
    // На большинстве платформ mdbx переотображает БД на месте; но на 32-битной
    // Windows нужный диапазон адресов после текущего отображения может
    // оказаться занятым (MDBX_UNABLE_EXTEND_MAPSIZE). Для этого случая
    // предусмотрен переносимый запасной путь: переоткрыть ту же БД с
    // увеличенной геометрией (новое отображение создаётся с нужным upper).
    mdbx::env::geometry bigger;
    bigger.size_upper = 8 * 1024 * 1024;
    try {
      env.set_geometry(bigger);
    } catch (const mdbx::db_unable_extend &) {
      mdbx::env::geometry reopened = geo;
      reopened.size_upper = bigger.size_upper;
      env.close();
      env = mdbx::env_managed(path, mdbx::env_managed::create_parameters().set_geometry(reopened),
                              mdbx::env::operate_parameters());
    }

    {
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      for (int i = 0; i < 2000; ++i) {
        const auto key = "k" + std::to_string(inserted + i);
        const std::string value(1024, 'y');
        txn.insert(table, mdbx::slice(key), mdbx::slice(value));
      }
      txn.commit();
    }
    std::cout << "grew geometry, continued: total " << (inserted + 2000) << " inserts\n";

    std::cout << "ok: MAP_FULL reproduced and resolved\n";
    return EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    std::cerr << "error: " << ex.what() << "\n";
    return EXIT_FAILURE;
  }
}