#pragma once
#include "overall_define.h"
#include <cassert>

template<typename Poker>
class Sequence{
public:
    using Poker_t = Poker;
public:
    explicit Sequence(int u);

    explicit Sequence(const char* str);

    /* get the id for this type of node */
    int  get_id() const;

    /* get next player*/
    int next_player() const;

    /* is certain state? */
    bool is_chance() const;

    bool is_terminal() const; // !is_terminal() == is_internal()

    /* is fold state? */
    bool is_fold() const;

    // /* who wins at a fold? */
    // bool only_one_survive() const;

    /* who wins at a fold? */
    int only_survivor() const;

    // /* pot at terminal */
    const type::chip_t* get_pot() const;

    // deal awarding
    void deal_showdown(const type::rank_t ranks[Poker_t::num_players], double award[Poker_t::num_players]) const;

    // deal awarding
    void deal_fold(double award[Poker_t::num_players]) const;

    /* do action at a non-chance state */
    Sequence<Poker_t> do_action(int v) const;

    std::string to_string() const;
public://偏特化函数


protected:
    int u; /* betting sequence for the game */
};

#include "util/ffs.h"
#include <cstring>
#include <functional>

template<typename Poker_t>
inline Sequence<Poker_t>::Sequence(int u) :u(u){
}

template<typename Poker_t>
inline Sequence<Poker_t>::Sequence(const char* str) {
    assert(Game<Poker_t>::initialized);
    assert(str);
    assert(*str == overall_define::ROUND_SEPERATOR);
    
    ++str;

    /* walk betting sequence str */
    u = 0;

    for(const char * p=str; *p;) {
        
        /* find next state */
        char action = *(p++);

        int i = 0;
        int next_u = -1;
        for(;i<Game<Poker_t>::num_actions[u]; ++i){
            next_u = Game<Poker_t>::transition[u][i];
            if(Game<Poker_t>::action_result_from[next_u]==action)
                break;
        }
        assert(i < Game<Poker_t>::num_actions[u]);
        assert(next_u < Game<Poker_t>::num_internal);

        /* should there be a round seperator too? */
        if (next_u < Game<Poker_t>::num_internal && Game<Poker_t>::round[next_u] > Game<Poker_t>::round[u]) {
            assert(*(p) == overall_define::ROUND_SEPERATOR);
            ++p;
        }

        u = next_u;
    } 
}

template<typename Poker_t>
std::string Sequence<Poker_t>::to_string() const {
    if (u) {
        /* 
        * trace up to root, looking what action takes us from each
        * parent to each child
        */
        std::string str;
        for(int v=Game<Poker_t>::parent[u],w=u; v!=-1; w=v,v=Game<Poker_t>::parent[v]) {

            /* check if we should add a round seperator */
            if (w < Game<Poker_t>::num_internal && Game<Poker_t>::round[v] < Game<Poker_t>::round[w]) {

                str.push_back(overall_define::ROUND_SEPERATOR);
            } 

            /* add the action */
            str.push_back(Game<Poker_t>::action_result_from[w]);
        }
        str.push_back(overall_define::ROUND_SEPERATOR);

        /* string is in the reverse order it needs to be in */
        std::reverse(str.begin(), str.end());
        return str;
    } else {
    
        /* root has empty betting string */
        return std::string(1, overall_define::ROUND_SEPERATOR);
    }
}

template<typename Poker_t>
inline bool Sequence<Poker_t>::is_chance() const {
    return u < Game<Poker_t>::num_chance;
}

template<typename Poker_t>
inline bool Sequence<Poker_t>::is_terminal() const {
    return u >= Game<Poker_t>::num_internal;
}

template<typename Poker_t>
const inline type::chip_t* Sequence<Poker_t>::get_pot() const {
    return Game<Poker_t>::pot[u-Game<Poker_t>::num_internal];
}

// template<typename Poker_t>
// inline bool Sequence<Poker_t>::only_one_survive() const {
//     int survivor_code = Game<Poker_t>::survivor_code[u];
//     bool only_one_survive_ = survivor_code > 0 && (survivor_code & (survivor_code - 1)) == 0;// 本身判断survivor_code是2的整数幂
//     return only_one_survive_;
// }

template<typename Poker_t>
inline bool Sequence<Poker_t>::is_fold() const {
    return is_terminal() && 
           !(Game<Poker_t>::whose_turn[u] < 0); // whose_turn == -1 表示showdown有多个存活， whose_turn>=0是fold此时whose_turn就是唯一存活的玩家
}

template<typename Poker>
inline int Sequence<Poker>::only_survivor() const {
    if(is_fold())
        return overall_define::ffs(Game<Poker_t>::survivor_code[u]);
    return -1;
}

template<typename Poker_t>
inline int Sequence<Poker_t>::get_id() const {
    return u;
}

template<typename Poker>
inline int Sequence<Poker>::next_player() const {
    assert(u<Game<Poker_t>::num_internal);
    int survivor_code = Game<Poker_t>::survivor_code[u];
    int cur_player = Game<Poker_t>::whose_turn[u];
    return next_player<Poker_t>(survivor_code, cur_player);
}

template<typename Poker_t>
inline void Sequence<Poker_t>::deal_showdown(const type::rank_t ranks[Poker_t::num_players], double award[Poker_t::num_players]) const {
    /* 获取pot*/
    type::chip_t pot[Poker_t::num_players];
    std::memcpy(pot, get_pot(), sizeof(type::chip_t)*Poker_t::num_players);

    /* 获取存活玩家rank*/
    type::rank_t alive_ranks[Poker_t::num_players];
    std::memcpy(alive_ranks, ranks, sizeof(type::rank_t)*Poker_t::num_players);
    const int survivor_code = Game<Poker_t>::survivor_code[u];
    for(int i = 0; i < Poker_t::num_players; ++i){
        if (!(survivor_code & (1<<i)))
            alive_ranks[i] = -1;
    }

    /* 找出所有玩家并按照投入pot的筹码数排序*/
    type::rank_t win_rank = std::numeric_limits<type::rank_t>::min();
    std::vector<std::pair<type::chip_t/*pot*/, int/*index*/>> sorted_pot;
    sorted_pot.reserve(Poker_t::num_players);
    for (int i = 0; i<Poker_t::num_players; ++i){
        if(win_rank > alive_ranks[i])
            continue;

        if (win_rank < alive_ranks[i]){
            win_rank = alive_ranks[i];
            sorted_pot.clear();
        }
        sorted_pot.push_back(std::make_pair(pot[i], i));
    }
    int num_winner = sorted_pot.size();
    if (num_winner > 1){
        std::sort(sorted_pot.begin(), sorted_pot.end(), [](const auto& l, const auto& r){
            return l.first < r.first;
        });
    }

    /*分配award*/
    std::memset(award, 0, sizeof(double)*Poker_t::num_players);
    for (int j = 0; j<num_winner; ++j){
        double side_pot = 0;
        int cur_player = sorted_pot[j].second;
        type::chip_t cur_pot = pot[cur_player];
        if(cur_pot<=0)
            continue;

        for(int i = 0; i<Poker_t::num_players; ++i){
            type::chip_t side_chip = pot[i] > cur_pot? cur_pot : pot[i];
            
            side_pot += side_chip;
            pot[i] -= side_chip;
            award[i] -= side_chip;
        }

        for(int i = j; i<num_winner; ++i){
            award[sorted_pot[i].second] += side_pot/(num_winner-j);
        }
    }
}

template<typename Poker_t>
inline void Sequence<Poker_t>::deal_fold(double award[Poker_t::num_players]) const {
    const type::chip_t *pot = get_pot();
    int survior = only_survivor();
    std::transform(pot, pot+Poker_t::num_players, award, std::negate<double>{});
    award[survior] += std::accumulate(pot, pot+Poker_t::num_players, 0);
}

template<typename Poker_t>
inline Sequence<Poker_t> Sequence<Poker_t>::do_action(int v) const {
    return Sequence<Poker_t>(Game<Poker_t>::transition[u][v]);
}