#pragma once
#include "template/domain_dependent/game_base.h"
extern "C" {
#include "hand-isomorphism/hand_index.h"
}
#include <cassert>

struct Numeral211Assist : public PokerAssistBase{

    inline static const std::string game_name = "Numeral211";

    inline static constexpr std::array deck = std::to_array<type::card_t>({  0,  1,  2,  3 // 2
                                                                          ,  4,  5,  6,  7 // 3
                                                                          ,  8,  9, 10, 11 // 4
                                                                          , 12, 13, 14, 15 // 5
                                                                          , 16, 17, 18, 19 // 6
                                                                          , 20, 21, 22, 23 // 7
                                                                          , 24, 25, 26, 27 // 8
                                                                          , 28, 29, 30, 31 // 9
                                                                          , 32, 33, 34, 35 // T
                                                                          , 36, 37, 38, 39 // A
                                                                          });
    
    inline static const int num_rounds = 3;
    inline static constexpr std::array deal_hole_round = std::to_array<int>({2, 0, 0});
    inline static constexpr std::array deal_board_round = std::to_array<int>({0, 1, 1});
    inline static constexpr std::array first_to_act = std::to_array<int>({0, 1, 1});
    inline static constexpr std::array bets_per_round = std::to_array<int>({4, 4, 4});
    inline static constexpr std::array bet_size = std::to_array<type::chip_t>({10, 20, 20});
    
    inline static const int num_players = 2;
    inline static constexpr std::array antes = std::to_array<type::chip_t>({5, 5}); //blind

    inline static constexpr int CHAR_TO_RANK[] = {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x30 */
        -1,4,   -1, -1, -1, -1, -1, -1, -1, -1,1,  3,   -1, -1, -1, -1, /* 0x40 */
        -1,2,   -1, -1,0,   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x60 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x70 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */
    };
    inline static constexpr int CHAR_TO_SUIT[] = {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x30 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x40 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50 */
        -1, -1, -1,3,  2,   -1, -1, -1,1,   -1, -1, -1, -1, -1, -1, -1, /* 0x60 */
        -1, -1, -1,0,   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x70 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */
    };
};

struct Numeral211 : public PokerBase{
private:
    using PokerAssist_t = Numeral211Assist;
public:
    inline static const std::string game_name = PokerAssist_t::game_name;

    inline static constexpr std::array deck = PokerAssist_t::deck;

    inline static const int num_rounds = PokerAssist_t::num_rounds;
    inline static constexpr std::array deal_hole_round = PokerAssist_t::deal_hole_round;
    inline static constexpr std::array deal_board_round = PokerAssist_t::deal_board_round;
    inline static constexpr std::array bets_per_round = PokerAssist_t::bets_per_round;
    inline static constexpr std::array first_to_act = PokerAssist_t::first_to_act;
    inline static constexpr std::array bet_size = PokerAssist_t::bet_size;

    inline static const int num_players = PokerAssist_t::num_players;
    inline static constexpr std::array antes = PokerAssist_t::antes;

    inline static const int* const CHAR_TO_RANK = PokerAssist_t::CHAR_TO_RANK;
    inline static const int* const CHAR_TO_SUIT = PokerAssist_t::CHAR_TO_SUIT;
//////////////////////////////////////////////////////////////////////////////////
    inline static constexpr std::array hole_len = get_hole_len<PokerAssist_t>();
    inline static constexpr std::array board_len = get_board_len<PokerAssist_t>();
    inline static constexpr std::array hand_len = get_hand_len<PokerAssist_t>();//std::to_array<int>({1, 2});
    inline static const int deck_len = PokerAssist_t::deck.size();
    //hunl deal_combine的长度 5; hunl single_deal_combine的长度 4
    inline static const int deal_combine_len = get_deal_combine_len<PokerAssist_t>();
    inline static const int single_deal_combine_len = get_single_deal_combine_len<PokerAssist_t>();
    //[2,2,(0);(0),(0),3;(0),(0),1;(0),(0),1] h1, h2, b; [2,(0);(0),3;(0),1;(0),1] h, b
    inline static constexpr std::array<type::card_t, deal_combine_len> deal_combine = get_deal_combine<PokerAssist_t>();
    inline static constexpr std::array<type::card_t, single_deal_combine_len> single_deal_combine = get_single_deal_combine<PokerAssist_t>();
    //这里是针对deal_combine的index [0, 2, 3, 4], [2, 3, 4, 5]; [0, 1, 2, 3], [1, 2, 3, 4]
    inline static constexpr std::array<int, num_rounds> deal_combine_begin_round = get_deal_combine_begin_round<PokerAssist_t>();
    inline static constexpr std::array<int, num_rounds> deal_combine_end_round =  get_deal_combine_end_round<PokerAssist_t>();
    inline static constexpr std::array<int, num_rounds> single_deal_combine_begin_round = get_single_deal_combine_begin_round<PokerAssist_t>();
    inline static constexpr std::array<int, num_rounds> single_deal_combine_end_round =  get_single_deal_combine_end_round<PokerAssist_t>();
    //这里指的是发过的牌数量
    inline static constexpr std::array<int, num_rounds> dealed_card_num_round =  get_dealed_card_num_round<PokerAssist_t>();
    inline static constexpr std::array<int, num_rounds> single_dealed_card_num_round =  get_single_dealed_card_num_round<PokerAssist_t>();
    //每轮pi_c的倒数，只是当前轮次发牌的可能性，不累积的
    inline static constexpr std::array<int, num_rounds> deal_combine_num_round = get_deal_combine_num_round<PokerAssist_t>();
    inline static constexpr std::array<int, num_rounds> single_deal_combine_num_round = get_single_deal_combine_num_round<PokerAssist_t>();
    // inline static constexpr std::array<int, num_rounds> opponents_holes_deal_combine_begin_round = get_opponents_holes_deal_combine_begin_round<PokerAssist_t>();
    inline static constexpr std::array<int, num_rounds> opponents_holes_deal_combine_end_round =  get_opponents_holes_deal_combine_end_round<PokerAssist_t>();//[1,1,1,1]
    inline static constexpr std::array<int, num_rounds> opponents_holes_after_single_deal_combine_num_round = get_opponents_holes_after_single_deal_combine_num_round<PokerAssist_t>();// [C(50,2), C(47,2),C(46,2),C(45,2)]
};

namespace numeral211{
    using Game_t = Game<Numeral211>;
}

namespace numeral211{
}
/////////////////////////////////////////////////////////////////////////////////////////////////


template<>
inline constexpr auto GameBase<Numeral211>::count_sequences() -> std::tuple<int, int, int, int, std::array<int,Poker_t::num_rounds>, std::array<int,Poker_t::num_rounds>, std::array<int,Poker_t::num_rounds>, std::array<int,Poker_t::num_rounds>> {
    int num_chance_ = 0, num_internal_ = 0, num_terminal_ = 0, num_total_ = 0;
    std::array<int, Poker_t::num_rounds> num_chance_round_{0}, num_internal_round_{0}, num_terminal_round_{0}, num_sequence_round_{0};

    auto count_sequences_ = [ &num_internal_, &num_terminal_, &num_chance_, &num_total_
                            , &num_chance_round_, &num_internal_round_, &num_terminal_round_, &num_sequence_round_]
                            (auto&& count_sequences_, int round, int raises, bool first_action, bool is_terminal) ->void {
        ++num_sequence_round_[round];

        if(is_terminal) {
            /* count this as an terminal sequence */
            ++num_terminal_;
            ++num_terminal_round_[round];
        }
        else {
            /* count this as an internal sequence */
            ++num_internal_;
            ++num_internal_round_[round];

            if(first_action) {
                ++num_chance_;
                ++num_chance_round_[round];
            }

            /* can we raise? */
            if (raises < Poker_t::bets_per_round[round]) {
                /* we can raise, take this action */
                count_sequences_(count_sequences_, round, raises+1, false, false);
            }
            
            /* we can always call */
            if (first_action) {
                /* call does not end round */
                count_sequences_(count_sequences_, round, raises, false, false);
            } else {
                /* call ends round */
                if (round==Poker_t::num_rounds-1) {
                    /* we've hit a showdown */
                    count_sequences_(count_sequences_, round, raises, false, true);
                } else {
                    /* move to next round */
                    count_sequences_(count_sequences_, round+1, 0, true, false);
                }
            }

            /* can we fold? */
            if (raises) {
                /* we can fold because we are faced with a bet */
                count_sequences_(count_sequences_, round, raises, false, true);
            } 
        }
    };

    count_sequences_(count_sequences_, /* round=*/0, /* raises=*/0, /* first_action*/true, /* is_terminal*/ false);
    num_total_ = num_internal_ + num_terminal_;

    return {num_chance_, num_internal_, num_terminal_, num_total_, num_chance_round_, num_internal_round_, num_terminal_round_, num_sequence_round_};
}

template<>
class Game<Numeral211> : public GameBase<Numeral211>{
    friend GameBase<Poker_t>;
private:

    static void count_actions_(int round, int raises, bool first_action, int& internal, int& chance);

    static int construct_sequences_( int player, int round, int raises, int survivor_code, type::chip_t pot[Poker_t::num_players]
                                   , bool first_action, bool is_terminal
                                   , int& internal, int& chance, int& terminal, int sequence_round[Poker_t::num_rounds]);
    
};

