#include <experimental/filesystem>
#include <iostream>
#include <libpmemobj++/make_persistent.hpp>
#include <libpmemobj++/make_persistent_array.hpp>
#include <libpmemobj++/p.hpp>
#include <libpmemobj++/persistent_ptr.hpp>
#include <libpmemobj++/pool.hpp>
#include <libpmemobj++/transaction.hpp>
#include <string>
#include <unistd.h>

// for simplicity, pool file and layout share this name
static const std::string example_name = "array-example";
static const unsigned num_array_elems = 10;

// shortcuts
namespace pmdk = pmem::obj;
namespace filesystem = std::experimental::filesystem::v1;

enum enum_field
{
    FREE = 0,
    WOOD
};

// pool's root object
struct Root
{
    // array of fields - this is probably wrong
    pmdk::persistent_ptr<enum_field[]> enum_board; 
    // array of persistent fields - this should be correct
    pmdk::persistent_ptr<pmdk::p<enum_field>[]> p_enum_board; 
};

using pool_t = pmdk::pool<Root>;

// forward declaration. creates a pool and allocats objects
void setup(pool_t& pool);

// actual interesting part
int main()
{
    pool_t pool;
    setup(pool);
    
    auto root = pool.get_root();

    // at this point, everything should be 0
    std::cout << "intially: enum_board[0]: " << root->enum_board[0] << "\n";
    std::cout << "intially: p_enum_board[0]: " << root->p_enum_board[0] << "\n";

    try
    {
        // change state
        pmdk::transaction::exec_tx(pool, [&] {
                root->enum_board[0] = WOOD;
                root->p_enum_board[0]= WOOD;

                // abort manually
                pmdk::transaction::abort(EINVAL);
        });
    } catch (pmem::manual_tx_abort e)
    {
        std::cerr << e.what() << "\n";
    }

    // everything should be back at the old state
    std::cout << "after abort: enum_board[0]: " << root->enum_board[0] << "\n";
    std::cout << "after abort: p_enum_board[0]: " << root->p_enum_board[0] << "\n";

    pool.close();
}

void setup(pool_t& pool)
{
    if (!filesystem::exists(example_name))
    {
        pool = pool_t::create(example_name, example_name, PMEMOBJ_MIN_POOL);
    }
    else
    {
        pool = pool_t::open(example_name, example_name);
    }

    auto root = pool.get_root();
    // allocate object if they do not exists already
    if (root->enum_board == nullptr)
    {
        pmdk::transaction::exec_tx(pool, [&] {
            root->enum_board = pmdk::make_persistent<enum_field[]>(num_array_elems);
            root->p_enum_board = pmdk::make_persistent<pmdk::p<enum_field>[]>(num_array_elems);
        });
    }
}
