// MAP_FULL reproduction and resolution via geometry growth
// Textbook section: Volume III, chapter 17, "Growth, shrinking and defragmentation", §17.3 "MAP_FULL".
// What it demonstrates: a small geometry + large volume → MDBX_MAP_FULL;
//   resolution by increasing the upper bound (set_geometry) and retrying.
//   On 32-bit Windows in-place remapping may be unavailable
//   (MDBX_UNABLE_EXTEND_MAPSIZE); the example then reopens the DB with an increased
//   geometry — the result is the same, but the new mapping is created directly with the needed upper.
// Build: g++ -std=c++17 -I<libmdbx-include> -I../common 23-map-full.c++ -lmdbx
// Run: ./23-map-full
// Expected output:
//   MDBX_MAP_FULL after <N> inserts
//   grew geometry, continued: total <M> inserts
//   ok: MAP_FULL reproduced and resolved
// On failure — message to stderr and 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);

    // A rigid geometry: the upper bound == the current size, only 512 KB.
    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";
    }

    // We increase the upper bound — and continue from the same point.
    // On most platforms mdbx remaps the DB in place; but on 32-bit Windows the
    // required address range after the current mapping may be occupied
    // (MDBX_UNABLE_EXTEND_MAPSIZE). For this case a portable fallback path is
    // provided: reopen the same DB with an increased geometry (the new mapping
    // is created with the needed 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;
  }
}