#pragma once
#include "overall_define.h"
#include <cstdio>
#include <forward_list>
#include <tlx/thread_pool.hpp>
#include "template/algorithm/iteration/iteration_util.hpp"

template<typename Poker>
class Abstraction{
public:

    using Poker_t = Poker;

public:

    Abstraction(std::function<void(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])> initializer);

    ~Abstraction();

    uint64_t round_bucket_size(int round) const;

    const uint64_t* bucket_sizes() const;

    uint64_t round_iso_size(int round) const;

    const uint64_t* iso_sizes() const;

    uint64_t abstract_view(const type::card_t* hole, const type::card_t* board, int round) const;

    uint64_t abstract_view(const Hand<Poker_t>& hand) const;

    uint64_t abstract_view(uint64_t priso, int round) const;

    const std::forward_list<std::pair<uint64_t, double>>& last_buckethits_rate(uint64_t bucket, int round) const;
    
    void print(FILE* stream) const;

    std::string to_string() const;

protected:
    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* iso_to_bucket_round[Poker_t::num_rounds];
    uint64_t* priso_to_xriso_round[Poker_t::num_rounds];
    // const static char* dir_path;
};

template<typename Poker>
Abstraction<Poker>::Abstraction(std::function<void(int [Poker_t::num_rounds], uint64_t [Poker_t::num_rounds], uint64_t [Poker_t::num_rounds], uint64_t* [Poker_t::num_rounds])> initializer) {
    initializer(recall_from, iso_size_round, bucket_size_round, iso_to_bucket_round);

    type::card_t hand_card[Poker_t::hand_len[Poker_t::num_rounds-1]];

    // 填充priso to xriso
    for (int r = 0; r < Poker_t::num_rounds; ++r) {
        uint64_t priso_size = Hand<Poker_t>::get_isomorphism_size(r, 0);
        priso_to_xriso_round[r] = new uint64_t[priso_size];
        for (uint64_t i = 0; i < priso_size; ++i) {
            Hand<Poker_t>::hand_unisomorphism(i, r, 0, hand_card);
            uint64_t xriso = Hand<Poker_t>::compute_hand_isomorphism(r, recall_from[r], hand_card);
            priso_to_xriso_round[r][i] = xriso;
        }
    }
}

template<typename Poker>
Abstraction<Poker>::~Abstraction() {
    for (int r = 0; r < Poker_t::num_rounds; ++r) {
        delete[] iso_to_bucket_round[r];
        delete[] priso_to_xriso_round[r];
    }
}

template<typename Poker>
inline const std::forward_list<std::pair<uint64_t, double>>& Abstraction<Poker>::last_buckethits_rate(uint64_t bucket, int round) const {
    return this->bucket_mapto_last_buckethits_rate_round[round][bucket];
}

template<typename Poker>
uint64_t Abstraction<Poker>::round_bucket_size(int round) const {
    assert(round<=Poker::num_rounds);
    return bucket_size_round[round];
}

template<typename Poker>
const uint64_t* Abstraction<Poker>::bucket_sizes() const {
    return bucket_size_round;
}

template<typename Poker>
uint64_t Abstraction<Poker>::round_iso_size(int round) const {
    assert(round<=Poker::num_rounds);
    return iso_size_round[round];
}

template<typename Poker>
const uint64_t* Abstraction<Poker>::iso_sizes() const {
    return iso_size_round;
}

template<typename Poker>
uint64_t Abstraction<Poker>::abstract_view(const type::card_t* hole, const type::card_t* board, int round) const{
    Hand<Poker> hand(hole, board, round);
    return iso_to_bucket_round[round][hand.get_hand_isomorphism(recall_from[round])];
}

template<typename Poker>
uint64_t Abstraction<Poker>::abstract_view(const Hand<Poker>& hand) const {
    return iso_to_bucket_round[hand.get_round()][hand.get_hand_isomorphism(recall_from[hand.get_round()])];
}

template<typename Poker>
uint64_t Abstraction<Poker>::abstract_view(uint64_t priso, int round) const {
    uint64_t xriso = priso_to_xriso_round[round][priso];
    return iso_to_bucket_round[round][xriso];
}

template<typename Poker>
void Abstraction<Poker>::print(FILE *stream) const {

    fprintf(stream, "Game: %s\n", Poker_t::game_name.data());

    for (int r = 0; r < Poker_t::num_rounds; ++r){
        fprintf(stream, "Round: %d; Recall from: %d; Isomorphism size: %ld; Bucket size: %ld\n", r, recall_from[r], iso_size_round[r], bucket_size_round[r]);
        fprintf(stream, "iso\t\tbucket\t\thand\n");

        for(uint64_t iso = 0; iso < iso_size_round[r]; ++iso) {
            fprintf(stream, "%9ld\t%9ld\n", iso, iso_to_bucket_round[r][iso]);
        }
    }
}

template<typename Poker>
std::string Abstraction<Poker>::to_string() const {
    return "暂时为空";
}
