/**
 *  Sometimes data model classes may have private/protected fields to
 *  be mapped to a storage. In this case you have to pass setter and getter
 *  function instead of member pointer. Also to specify a column in select
 *  or where condition you have to add getter or setter function pointer. Both of them
 *  are mapped to the same column.
 *  getter must have definition like this: `const F& O::*() const`
 *  setter: `void O::*(F)`
 */

#include <sqlite_orm/sqlite_orm.h>
#include <iostream>
#include <cassert>

using std::cout;
using std::endl;

class Player {
    int id = 0;
    std::string name;

  public:
    Player() {}

    Player(std::string name_) : name(std::move(name_)) {}

    Player(int id_, std::string name_) : id(id_), name(std::move(name_)) {}

    std::string getName() const {
        return this->name;
    }

    void setName(std::string name) {
        this->name = std::move(name);
    }

    int getId() const {
        return this->id;
    }

    void setId(int id) {
        this->id = id;
    }
};

int main(int, char**) {
    using namespace sqlite_orm;
    auto storage = make_storage(
        "private.sqlite",
        make_table(
            "players",
            make_column("id",
                        &Player::setId,  //  setter
                        &Player::getId,  //  getter
                        primary_key()),
            make_column("name",
                        &Player::getName,  //  BTW order doesn't matter: setter can be placed before getter or opposite.
                        &Player::setName)));
    storage.sync_schema();
    storage.remove_all<Player>();

    auto soloId = storage.insert(Player("Solo"));

    auto playersCount = storage.count<Player>();
    cout << "players count = " << playersCount << endl;
    assert(playersCount == 1);

    cout << "solo = " << storage.dump(storage.get<Player>(soloId)) << endl;

    auto deadpoolId = storage.insert(Player("Deadpool"));

    cout << "deadpool = " << storage.dump(storage.get<Player>(deadpoolId)) << endl;

    playersCount = storage.count<Player>();
    cout << "players count = " << playersCount << endl;
    assert(playersCount == 2);

    auto idsOnly = storage.select(&Player::getId);  //  or storage.select(&Player::setId);
    cout << "idsOnly count = " << idsOnly.size() << endl;

    auto somePlayers = storage.get_all<Player>(where(lesser_than(length(&Player::getName), 5)));
    cout << "players with length(name) < 5 = " << somePlayers.size() << endl;
    assert(somePlayers.size() == 1);
    for(auto& player: somePlayers) {
        cout << storage.dump(player) << endl;
    }
}
