#pragma once
#include "game_numeral211.h"
#include "template/domain_dependent/game_base.hpp"
#include "numeral211/hand-isomorphism/deck.h"
#include <cstring>

template<>
void deal_all_hand<Numeral211>(type::card_t (*holes)[Numeral211::hole_len[Numeral211::num_rounds-1]], type::card_t* board) {
    using Poker_t = Numeral211;
    static int deck[52] = {  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 // J
                          , 40, 41, 42, 43 // Q
                          , 44, 45, 46, 47 // K
                          , 48, 49, 50, 51 // A
                          };
    
    //每一次调用都把前total_hand_len换一遍
    static const int last_round = Poker_t::num_rounds-1;
    for( int i = 0, total_deal = Poker_t::num_players*Poker_t::hole_len[last_round] + Poker_t::board_len[last_round]
       ; i < total_deal
       ; ++i){
        std::swap(deck[i], deck[i + overall_define::mt_rand()%(CARDS-i)]);
    }

    // 发玩家牌
    static const int total_hole_len = Poker_t::hole_len[last_round];
    for( int p = 0; p < Poker_t::num_players; ++p) {
        std::copy(deck + p*total_hole_len, deck + (p+1)*total_hole_len, holes[p]);
    }

    // 发board
    static const int total_board_len = Poker_t::board_len[last_round];
    std::copy( deck + total_hole_len*Poker_t::num_players
             , deck + total_hole_len*Poker_t::num_players + total_board_len
             , board);
}

template<>
std::string card_to_string<Numeral211>(type::card_t card) {
    return std::string(1, SUIT_TO_CHAR[card%SUITS])+std::string(1, RANK_TO_CHAR[card/SUITS]);
}
/////////////////////////////////////////////////////////////////////////////////////////////////

template<>
void GameBase<Numeral211>::count_actions(){
    int internal = 0, chance = 0;
    Game<Poker_t>::count_actions_(/* round=*/0, /* raises=*/0, /* first_action*/true, /* &internal=*/internal, /* &chance=*/chance);
}

template<>
void GameBase<Numeral211>::construct_sequences(){
    int sequence_round[Poker_t::num_rounds]{0};
    int internal = 0, chance = 0, terminal = 0;
    std::array k = Poker_t::antes;
    Game<Poker_t>::construct_sequences_( /* player=*/Poker_t::first_to_act[0]
                                       , /* round=*/0
                                       , /* raises=*/0
                                       , /* survivor_code=*/ (1<<Poker_t::num_players)-1
                                       , /* pot[Poker_t::num_players]=*/k.data()
                                       , /* first_action=*/true
                                       , /* is_terminal*/false
                                       , /* &internal=*/internal
                                       , /* &chance=*/chance
                                       , /* &terminal=*/terminal
                                       , /* sequence_round[]=*/sequence_round);
}

/////////////////////////////////////////////////////////////////////////////////////////////////

void Game<Numeral211>::count_actions_(int round, int raises, bool first_action, int& internal, int& chance){
    /* count this as an internal sequence */
    int u;
    if(first_action){
        u = chance++;
    }
    else{
        u = num_chance + internal - chance;
    }
    internal++;

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

    /* can we fold? */
    if (raises) {
        /* we can fold because we are faced with a bet */
        ++num_actions_[u];
    } 
}

int Game<Numeral211>::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]) {

    int u;
    int current_round_node_idx = sequence_round[round]++;
    int action_idx = 0;

    if (is_terminal){
        u = num_internal + terminal++;
        std::memcpy(pot_[u-num_internal], pot, sizeof(type::chip_t)*Poker_t::num_players);
    }
    else {
        if(first_action){
            u = chance++;
        }
        else{
            u = num_chance + internal - chance;
        }
        internal++;
        int a = 0;

        /* how much is to call? */
        int opponent = next_player<Poker_t>(survivor_code, player);
        int to_call = pot[opponent] - pot[player];

        assert(to_call >= 0);

        /* can we raise? */
        if (raises < Poker_t::bets_per_round[round]) {

            /* make raise and construct child sequence */
            pot[player] += to_call + Poker_t::bet_size[round];
            int v = construct_sequences_( opponent, round, raises+1, survivor_code, pot
                                        , /* first_action=*/false, /* is_terminal=*/false
                                        , internal, chance, terminal, sequence_round);
            
            /* undo raise to pot */
            pot[player] -= to_call + Poker_t::bet_size[round];

            /* set transition for us and parent for child sequence */
            transition_[u][a++] = v;
            parent_[v]        = u;
            action_result_from_[v] = 'r';
            action_result_from_idx_[v] = action_idx++;
        } else {
            /* cannot raise */
        }

        /* can always call */
        if (first_action) {

            // assert(pot[0] == pot[1]);

            /* call does not end round, construct child sequence */
            pot[player] += to_call;
            int v = construct_sequences_( opponent, round, /* raises=*/0, survivor_code, pot
                                        , /* first_action=*/false, /* is_terminal=*/false
                                        , internal, chance, terminal, sequence_round);
            pot[player] -= to_call;
            
            /* set transition for us and parent for child sequence */
            transition_[u][a++] = v;
            parent_[v]        = u;
            action_result_from_[v] = round ==0 ?'c':'k';
            action_result_from_idx_[v] = action_idx++;
        } else {
            
            /* call ends round */
            if (round==Poker_t::num_rounds-1) {

                /* last round => showdown */
                pot[player] += to_call;
                int v = construct_sequences_( /* player*/-1, round, raises, survivor_code, pot
                                            , /* first_action=*/false, /* is_terminal=*/true
                                            , internal, chance, terminal, sequence_round);
                // win_amount_[v-num_internal_] = pot[player];
                /* undo call to pot */
                pot[player] -= to_call;
                
                /* set transition and parent */
                transition_[u][a++]       = v;
                parent_[v] = u;
                action_result_from_[v] = 'c';
                action_result_from_idx_[v] = action_idx++;
            } else {

                /* move to next round, construct child sequence */
                pot[player] += to_call;
                int v = construct_sequences_( Poker_t::first_to_act[round+1], round+1, /* raises=*/0, survivor_code, pot
                                            , /* first_action=*/true, /* is_terminal=*/false
                                            , internal, chance, terminal, sequence_round);

                /* undo call to pot */
                pot[player] -= to_call;

                /* set transition for us and parent for child sequence */
                transition_[u][a++] = v;
                parent_[v]        = u;
                action_result_from_[v] = 'c';
                action_result_from_idx_[v] = action_idx++;
            }
        }

        /* can we fold? */
        if (raises) {
            
            assert(to_call);

            /* create terminal state */
            int v = construct_sequences_( opponent, round, raises, survivor_code & ~(1<<player), pot
                                        , /* first_action=*/false, /* is_terminal=*/true
                                        , internal, chance, terminal, sequence_round);
            // win_amount_[v-num_internal_] = pot[player];
            
            /* set transition and parent */
            transition_[u][a++]       = v;
            parent_[v] = u;
            action_result_from_[v] = 'f';
            action_result_from_idx_[v] = action_idx++;
        } else {
            /* cannot fold */
        }
        assert(a == num_actions_[u]);
    }

    Game<Poker_t>::round_[u] = round;
    Game<Poker_t>::survivor_code_[u] = survivor_code;
    sequence_list_round_[round][current_round_node_idx] = u;
    whose_turn_[u]   = player;
    return u;
}