#pragma once
#include <filesystem>
#include <yaml-cpp/yaml.h>
#include "rocksdb/db.h"
using namespace ROCKSDB_NAMESPACE;

template<typename Poker_t>
class SuiteAbstr {
    std::size_t recall_froms[Poker_t::num_rounds];
    std::string db_path;
public:
    SuiteAbstr(const char* path): db_path(path){
        this->print_config();
        try {
            std::filesystem::path filepath = std::filesystem::path(db_path) / "alg_configs.yaml";

            YAML::Node config = YAML::LoadFile(filepath);
            assert(Poker_t::num_rounds == config["alg_configs"].size());
            for (size_t street = 0; street < config["alg_configs"].size(); ++street) {
                YAML::Node alg = config["alg_configs"][street];
                recall_froms[street] = alg["recall_from"].as<size_t>();
            }
        } catch (const std::exception& e) {
            std::cerr << "Exception caught: " << e.what() << std::endl;
        }
    }

    void print_config(){

        std::filesystem::path filepath = std::filesystem::path(db_path) / "alg_configs.yaml";
        std::cout<< "filepath: "<< filepath<< std::endl;
        // 打开文件
        std::ifstream file(filepath);
        if (!file.is_open()) {
            std::cerr << "Failed to open file: " << filepath << std::endl;
            assert(0); // 文件打开失败，返回错误码
        }

        std::string line;
        // 逐行读取文件内容并打印
        while (std::getline(file, line)) {
            std::cout << line << std::endl;
        }

        // 关闭文件
        file.close();
    }

    void operator()(int recall_from[Poker_t::num_rounds], uint64_t iso_size_round[Poker_t::num_rounds], uint64_t bucket_size_round[Poker_t::num_rounds], uint64_t* street_bucket[Poker_t::num_rounds]) const {
        std::copy(std::begin(recall_froms), std::end(recall_froms), recall_from);

        for(int r = 0; r < Poker_t::num_rounds; ++r){
            iso_size_round[r] = Hand<Poker_t>::get_isomorphism_size(r, recall_froms[r]);
            street_bucket[r] = new uint64_t[iso_size_round[r]];
        }

        std::string tables_name[Poker_t::num_rounds];
        for (int r = 0; r < Poker_t::num_rounds; ++r) {
            tables_name[r] = std::string("street") + std::to_string(r);
        }

        rocksdb::DB* db;
        rocksdb::DBOptions options;
        options.create_if_missing = false; // do not create database if it does not exist

        // open DB with two column families
        std::vector<ColumnFamilyDescriptor> column_families;
        // open the new one, too
        for(int r = 0; r < Poker_t::num_rounds; ++r){
            column_families.push_back(ColumnFamilyDescriptor(
                tables_name[r], ColumnFamilyOptions()));
        }
        // have to open default column family
        column_families.push_back(ColumnFamilyDescriptor(
            kDefaultColumnFamilyName, ColumnFamilyOptions()));
        std::vector<ColumnFamilyHandle*> handles;
        Status s = DB::OpenForReadOnly(DBOptions(), db_path, column_families, &handles, &db);
        assert(s.ok());

        rocksdb::ReadOptions read_options;
        bool need_bswap;
        for (int r = 0; r < Poker_t::num_rounds; ++r) {
            rocksdb::Iterator* it = db->NewIterator(read_options, handles[r]);
            {// 判断要不要换端
                it->SeekToFirst();
                it->Next();
                rocksdb::Slice key1 = it->key();
                uint32_t key1_uint32 = *(reinterpret_cast<const uint32_t*>(key1.data()));
                need_bswap = key1_uint32==1? false : true;
                assert((!need_bswap && key1_uint32==1)||
                       (need_bswap && __builtin_bswap32(key1_uint32)==1));
            }

            //最后一位是bucket_size
            it->SeekToLast();
            rocksdb::Slice last_key = it->key();
            rocksdb::Slice last_value = it->value();
            uint32_t iso_size = *(reinterpret_cast<const uint32_t*>(last_key.data()));
            uint32_t bucket_size = *(reinterpret_cast<const uint32_t*>(last_value.data()));
            if (need_bswap) {
                iso_size = __builtin_bswap32(iso_size);
                bucket_size = __builtin_bswap32(bucket_size);
            }
            bucket_size_round[r] = bucket_size;

            uint32_t key_uint32, value_uint32;
            for (it->Prev(); it->Valid(); it->Prev()) {
                rocksdb::Slice key = it->key();
                rocksdb::Slice value = it->value();

                key_uint32 = *(reinterpret_cast<const uint32_t*>(key.data()));
                value_uint32 = *(reinterpret_cast<const uint32_t*>(value.data()));
                if (need_bswap) {
                    key_uint32 = __builtin_bswap32(key_uint32);
                    value_uint32 = __builtin_bswap32(value_uint32);
                }
                street_bucket[r][key_uint32] = value_uint32;
            }
        }

        for (auto handle : handles) {
            db->DestroyColumnFamilyHandle(handle);
        }
        // Close the database
        delete db;
    }
};