#pragma once
#include "template/domain_independent/strategy.hpp"
#include "template/domain_independent/player_terminal_reach.hpp"
#include "template/domain_dependent/evaluator_base.h"
#include "util/combinatorics.h"
#include "template/algorithm/iteration/iteration_util.hpp"
#include <vector>
#include <cstring>
#include <forward_list>
#include <array>
//debug
#include <set>

template<typename Poker>
class ExpectValue;

template<typename Poker>
class ExpectValue{
public:
    using Poker_t = Poker;
    ExpectValue();
    ~ExpectValue();

    // void compute_evs(const Strategy<Poker_t>* strategies[Poker_t::num_players], double evs[Poker_t::num_players]);
    void compute_evs(const PlayerTerminalReach<Poker_t>* const player_terminal_reaches[Poker_t::num_players], double evs[Poker_t::num_players]);

protected:
    void clear_();
    void init_();
    void handle_terminal_(const PlayerTerminalReach<Poker_t>* const player_terminal_reaches[Poker_t::num_players], type::card_t holes[Poker_t::num_players][Poker_t::hole_len[Poker_t::num_rounds-1]], type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]]);
    void iterate_current_round_seqs_();
    bool update_and_still_in_current_round_();
    void pass_up_chance_();

protected:
    typedef double awards_t[Poker_t::num_players];
    awards_t** seqs_awards;//[sequence][isomorphism]
    int *unhandled_successor;//[Game<Poker_t>::num_total]
    std::forward_list<int> unhandled_seqs_round[Poker_t::num_rounds];
    std::vector<int> tohandle_seqs_round[Poker_t::num_rounds];
    int current_round;

protected:
    const std::array<std::forward_list<int>, Poker_t::num_rounds> seqs_result_from_last_round/*[Poker_t::num_rounds]*/ = iteration::get_seqs_result_from_last_round<Poker_t>();
};

template<typename Poker>
ExpectValue<Poker>::ExpectValue(){
    unhandled_successor = new int[Game<Poker_t>::num_total];

    seqs_awards = new awards_t*[Game<Poker_t>::num_total+1];//Game<Poker_t>::num_total位置存的是全部evs的加权和，isomorphism == 1
    std::memset(seqs_awards, 0, sizeof(awards_t*) * (Game<Poker_t>::num_total+1));
}

template<typename Poker>
ExpectValue<Poker>::~ExpectValue(){
    clear_();
    delete[] seqs_awards;
    delete[] unhandled_successor;
}

template<typename Poker>
void ExpectValue<Poker>::clear_(){

    std::memset(unhandled_successor, 0, sizeof(int)*Game<Poker_t>::num_total);

    for(int i = 0; i < Game<Poker_t>::num_total+1; ++i){
        if(seqs_awards[i])
            delete[] seqs_awards[i];
    }

    std::memset(seqs_awards, 0, sizeof(awards_t*)*(Game<Poker_t>::num_total+1));

    for(int i = 0; i < Poker_t::num_rounds; ++i){
        unhandled_seqs_round[i].clear();
        tohandle_seqs_round[i].clear();
    }
}

template<typename Poker>
void ExpectValue<Poker>::init_(){
    current_round = Poker_t::num_rounds-1;

    for(int i = 0; i<Game<Poker_t>::num_total; ++i){
        int round = Game<Poker_t>::round[i];
        unhandled_seqs_round[round].push_front(i);
    }

    std::memset(unhandled_successor, 0, sizeof(int) * Game<Poker_t>::num_total); //对所有seq都赋值0
    for(int i = 0; i<Game<Poker_t>::num_internal; ++i){ //只对前num_internal初始化动作数，后terminal个都是0
        unhandled_successor[i] = Game<Poker_t>::num_actions[i];
    }

    for(int r = Poker_t::num_rounds-1; r>=0; --r){
        for(auto bt = unhandled_seqs_round[r].before_begin(), it = std::next(bt), et = unhandled_seqs_round[r].end(); it!=et; it = std::next(bt)) {
            if(*it >= Game<Poker_t>::num_internal){
                tohandle_seqs_round[r].push_back(*it);
                unhandled_seqs_round[r].erase_after(bt); 
            }
            else{
                ++bt;
            }
        }
    }
}

// inline int cnt_terminal = 0;
// inline int cnt_chance = 0;
// std::vector<std::array<double,2>> a{};

template<typename Poker>
void ExpectValue<Poker>::compute_evs(const PlayerTerminalReach<Poker_t>* const player_terminal_reaches[Poker_t::num_players], double evs[Poker_t::num_players]) {
    clear_();
    init_();

    while(current_round >= 0){
        printf("========================================================\n");
        // 处理本层terminal
        iteration::dealing_upto_current_round_hands<Poker_t>(std::bind(&ExpectValue<Poker>::handle_terminal_, this, player_terminal_reaches, std::placeholders::_1, std::placeholders::_2), current_round);
        // printf("after terminal ========================================================\n");
        // int isomorphism_size = Poker_t::num_hand_isomorphism_round[current_round];
        // a.clear();
        // a.resize(isomorphism_size);
        // for(int u : tohandle_seqs_round[current_round]){
        //     for(int j = 0; j<isomorphism_size; ++j){
        //         for(int i = 0; i < Poker_t::num_players; ++i){
        //             a[j][i] += seqs_awards[u][j][i];
        //         }
        //     }
        // }
        // for(int j = 0; j<a.size(); ++j){
        //     for(int i = 0; i < Poker_t::num_players; ++i){
        //         printf("%lf\t", a[j][i]);
        //     }
        //     printf("\n");
        // }

        // 在当前层不断往前回滚
        do {
            iterate_current_round_seqs_();
        } while(update_and_still_in_current_round_());

        // printf("after回滚========================================================\n");
        // int next_round = current_round +1;
        // isomorphism_size = Poker_t::num_hand_isomorphism_round[next_round];
        // a.clear();
        // a.resize(isomorphism_size);
        // for(int u : seqs_result_from_last_round[next_round]){
        //     for(int j = 0; j<isomorphism_size; ++j){
        //         for(int i = 0; i < Poker_t::num_players; ++i){
        //             a[j][i] += seqs_awards[u][j][i];
        //         }
        //     }
        // }
        // for(int j = 0; j<a.size(); ++j){
        //     for(int i = 0; i < Poker_t::num_players; ++i){
        //         printf("%lf\t", a[j][i]);
        //     }
        //     printf("\n");
        // }
        
        // 将本层chance传递到上一层
        pass_up_chance_();
        // printf("after chance ========================================================\n");

        // std::set<int> sst;
        // for(const int nxt : seqs_result_from_last_round[current_round+1]) {
        //     int parent = Game<Poker_t>::parent[nxt];
        //     parent = parent<0? Game<Poker_t>::num_total:parent;
        //     if(!sst.contains(parent)){
        //         sst.insert(parent);
        //     }
        // }

        // isomorphism_size = current_round<0? 1 : Poker_t::num_hand_isomorphism_round[current_round];
        // a.clear();
        // a.resize(isomorphism_size);
        // for(int u : sst){
        //     for(int j = 0; j<isomorphism_size; ++j){
        //         for(int i = 0; i < Poker_t::num_players; ++i){
        //             a[j][i] += seqs_awards[u][j][i];
        //         }
        //     }
        // }
        // for(int j = 0; j<a.size(); ++j){
        //     for(int i = 0; i < Poker_t::num_players; ++i){
        //         printf("%lf\t", a[j][i]);
        //     }
        //     printf("\n");
        // }
        // printf("round %d over ==========================================\n", current_round+1);
    }
    std::copy(seqs_awards[Game<Poker_t>::num_total][0], seqs_awards[Game<Poker_t>::num_total][0]+Poker_t::num_players,evs);
}

template<typename Poker>
void ExpectValue<Poker>::handle_terminal_(const PlayerTerminalReach<Poker_t>* const player_terminal_reaches[Poker_t::num_players], type::card_t holes[Poker_t::num_players][Poker_t::hole_len[Poker_t::num_rounds-1]], type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]]) {
    Hand<Poker_t> temp_hand(holes[0], board, current_round);
    uint64_t round_buckets[Poker_t::num_players];
    round_buckets[0] = temp_hand.get_hand_isomorphism(0);
    for(int i = 1; i<Poker_t::num_players; ++i){
        temp_hand.change_hole(holes[i]);
        round_buckets[i] = temp_hand.get_hand_isomorphism(0);
    }
//////////////////////////////////////////////////////////////////
    uint64_t isomorphism_size = Hand<Poker_t>::get_isomorphism_size(current_round, 0);
    int ranks[Poker_t::num_players];
    Evaluator<Poker_t>::evaluate_ranks(holes, board, ranks);

    double awards[Poker_t::num_players];
    for(const int u : tohandle_seqs_round[current_round]){
        Sequence<Poker_t> seq(u);
        assert(seq.is_terminal());
        assert(Game<Poker_t>::round[u] == current_round);
        if(seq.is_fold()){
            seq.deal_fold(awards);
        }
        else {
            seq.deal_showdown(ranks, awards);
        }
        if(!seqs_awards[u]){
            seqs_awards[u] = new double[isomorphism_size][Poker_t::num_players];
            std::memset(seqs_awards[u], 0, sizeof(double[Poker_t::num_players]) * isomorphism_size);
        }
        double pr = 1.;
        for (int i = 0; i < Poker_t::num_players; ++i){
            pr *= player_terminal_reaches[i]->get_reach(u, round_buckets[i]);
        }

        for (int i = 0; i < Poker_t::num_players; ++i){
            seqs_awards[u][round_buckets[i]][i] += pr * awards[i];
            // seqs_awards[u][round_buckets[i]][i] += pr;
        }
    }
}

template<typename Poker>
void ExpectValue<Poker>::iterate_current_round_seqs_() {

    uint64_t isomorphism_size = Hand<Poker_t>::get_isomorphism_size(current_round, 0);

    for(const int u : tohandle_seqs_round[current_round]){
        int parent = Game<Poker_t>::parent[u];

        if(parent == -1 || Game<Poker_t>::round[parent] != current_round)
            continue;

        if(!seqs_awards[parent]){
            seqs_awards[parent] = new double[isomorphism_size][Poker_t::num_players];
            std::memset(seqs_awards[parent], 0, sizeof(double[Poker_t::num_players]) * isomorphism_size);
        }

        for(uint64_t i = 0; i<isomorphism_size; ++i){
            for(int p = 0; p<Poker_t::num_players; ++p)
                seqs_awards[parent][i][p] += seqs_awards[u][i][p];
        }

        delete[] seqs_awards[u];
        seqs_awards[u] = nullptr;
    }
}

template<typename Poker>
bool ExpectValue<Poker>::update_and_still_in_current_round_() {

    // 层迭代之后，迭代节点的后继就减少了
    for(const int u : tohandle_seqs_round[current_round]) {
        int parent = Game<Poker_t>::parent[u];
        if(parent != -1){
            --unhandled_successor[parent];
            assert(unhandled_successor[parent]>=0);
        }
    }

    // 本轮次后继节点为0的点进入下次迭代
    tohandle_seqs_round[current_round].clear();
    for(auto bt = unhandled_seqs_round[current_round].before_begin(), it = std::next(bt), et = unhandled_seqs_round[current_round].end(); it!=et; it = std::next(bt)) {
        int u = *it;
        if(unhandled_successor[u] == 0){
            tohandle_seqs_round[current_round].push_back(u);
            unhandled_seqs_round[current_round].erase_after(bt); 
        }
        else{
            ++bt;
        }
    }

    if(tohandle_seqs_round[current_round].empty()){
        --current_round;
        return false;
    }
    else{
        return true;
    }
}

template<typename Poker>
void ExpectValue<Poker>::pass_up_chance_(){
    
    uint64_t current_iso_size = current_round < 0? 1: Hand<Poker_t>::get_isomorphism_size(current_round, 0);

    int next_round = current_round + 1;
    uint64_t next_iso_size = Hand<Poker_t>::get_isomorphism_size(next_round, 0);
    type::card_t next_hand_c[Poker_t::hand_len[Poker_t::num_rounds-1]];

    // 先给上一层分配空间
    for(int nxt : seqs_result_from_last_round[next_round]) {
        int parent = Game<Poker_t>::parent[nxt] < 0 ? Game<Poker_t>::num_total : Game<Poker_t>::parent[nxt];

        if(!seqs_awards[parent]){
            seqs_awards[parent] = new double[current_iso_size][Poker_t::num_players];
            std::memset(seqs_awards[parent], 0, sizeof(double[Poker_t::num_players]) * current_iso_size);
        }
    }

    // 向上传递
    for (uint64_t next_isomorphism = 0; next_isomorphism < next_iso_size; ++next_isomorphism){
        
        //计算上一层的idx
        uint64_t current_isomorphism;
        if(current_round<0) {
            current_isomorphism = 0;
        }
        else {
            Hand<Poker_t>::hand_unisomorphism(next_isomorphism, next_round, 0, next_hand_c);
            Hand<Poker_t> current_hand(next_hand_c, next_hand_c + Poker_t::hole_len[next_round], current_round);
            current_isomorphism = current_hand.get_hand_isomorphism(0);
        }

        for(int nxt : seqs_result_from_last_round[next_round]) {
            int parent = Game<Poker_t>::parent[nxt] < 0 ? Game<Poker_t>::num_total : Game<Poker_t>::parent[nxt];

            for(int p = 0; p<Poker_t::num_players; ++p) {
                seqs_awards[parent][current_isomorphism][p] += seqs_awards[nxt][next_isomorphism][p];
            }
        }
    }

    //释放下一层空间，并乘发牌权重
    for(int nxt : seqs_result_from_last_round[next_round]) {
        int parent = Game<Poker_t>::parent[nxt] < 0 ? Game<Poker_t>::num_total : Game<Poker_t>::parent[nxt];

        for(uint64_t current_isomorphism = 0; current_isomorphism < current_iso_size; ++current_isomorphism){
            for(int p = 0; p<Poker_t::num_players; ++p) {
                seqs_awards[parent][current_isomorphism][p] /= Poker_t::deal_combine_num_round[next_round];
            }
        }
        delete[] seqs_awards[nxt];
        seqs_awards[nxt] = nullptr;
    }
}
