#pragma once
#include "numeral211/evaluator_numeral211.h"
#include "template/domain_dependent/evaluator_base.hpp"
#include <fstream>


////////////////////////////////////////////////////////////////////////////////////
template<>
void EvaluatorBase<Numeral211>::init(){
    std::ifstream ifs;
    ifs.open("data/2plus2/RhodeIslandHandRanks.dat", std::ios::binary);
    if(!ifs.is_open()) {
        printf("cant find RhodeIslandHandRanks.dat file\n");
        throw "cant find RhodeIslandHandRanks.dat file";
    }
    ifs.read((char*)(&Evaluator<Numeral211>::lookup_2plus2), Evaluator<Numeral211>::len_2plus2*sizeof(int));
    ifs.close();
    initialized_ = true;
}

template<>
void EvaluatorBase<Numeral211>::free(){
}

template<>
type::rank_t EvaluatorBase<Numeral211>::evaluate_rank(const type::card_t hole[Poker_t::hole_len[Poker_t::num_rounds-1]], const type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]])
{
    type::rank_t rk = 53;
    for (int i = 0, showdown_blen = Poker_t::board_len[Poker_t::num_rounds-1]; i < showdown_blen; ++i){
        rk = Evaluator<Numeral211>::lookup_2plus2[rk+(board[i]+(52-CARDS)+1)];
    }
    for (int i = 0, showdown_hlen = Poker_t::hole_len[Poker_t::num_rounds-1]; i < showdown_hlen; ++i){
        rk = Evaluator<Numeral211>::lookup_2plus2[rk+(hole[i]+(52-CARDS)+1)];
    }
    rk = Evaluator<Numeral211>::lookup_2plus2[rk];
    return rk;
}

template<>
type::rank_t EvaluatorBase<Numeral211>::evaluate_rank(const type::card_t hand[Poker_t::hand_len[Poker_t::num_rounds-1]])
{
    type::rank_t rk = 53;
    for (int i = 0, showdown_len = Poker_t::hand_len[Poker_t::num_rounds-1]; i < showdown_len; ++i){
        rk = Evaluator<Numeral211>::lookup_2plus2[rk+(hand[i]+(52-CARDS)+1)];
    }
    rk = Evaluator<Numeral211>::lookup_2plus2[rk];
    return rk;
}

template<>
void EvaluatorBase<Numeral211>::evaluate_ranks(const type::card_t holes[Poker_t::num_players][Poker_t::hole_len[Poker_t::num_rounds-1]], const type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]], type::rank_t ranks[Poker_t::num_players]){

    type::rank_t rk = 53;
    for (int i = 0, showdown_blen = Poker_t::board_len[Poker_t::num_rounds-1]; i < showdown_blen; ++i){
        rk = Evaluator<Numeral211>::lookup_2plus2[rk+(board[i]+(52-CARDS)+1)];
    }
    for (int j = 0; j < Poker_t::num_players; ++j){
        int rkj = rk;
        for (int i = 0, showdown_hlen = Poker_t::hole_len[Poker_t::num_rounds-1]; i < showdown_hlen; ++i){
            rkj = Evaluator<Numeral211>::lookup_2plus2[rkj+(holes[j][i]+(52-CARDS)+1)];
        }
        ranks[j] = Evaluator<Numeral211>::lookup_2plus2[rkj];
    }
}

template<>
std::string EvaluatorBase<Numeral211>::info_rank(const type::card_t hole[Poker_t::hole_len[Poker_t::num_rounds-1]], const type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]])
{
    type::rank_t rk = evaluate_rank(hole, board);
    std::string info = (Evaluator<Poker_t>::info_format % (rk >> 12) % (rk & 0x00000fff) % rk % Evaluator<Poker_t>::hand_name[rk >> 12]).str();
    return info;
}

template<>
std::string EvaluatorBase<Numeral211>::info_rank(const type::card_t hand[Poker_t::hand_len[Poker_t::num_rounds-1]])
{
    type::rank_t rk = evaluate_rank(hand);
    std::string info = (Evaluator<Poker_t>::info_format % (rk >> 12) % (rk & 0x00000fff) % rk % Evaluator<Poker_t>::hand_name[rk >> 12]).str();
    return info;
}

template<>
ShowdownHistogram EvaluatorBase<Numeral211>::evaluate_potential(const type::card_t hole[Poker_t::hole_len[Poker_t::num_rounds-1]], const type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]]){
    ShowdownHistogram showdown_histogram{0};

    // 拿到当前牌的位置
    type::rank_t rk = 53;
    for (int i = 0, showdown_blen = Poker_t::board_len[Poker_t::num_rounds-1]; i < showdown_blen; ++i){
        rk = Evaluator<Numeral211>::lookup_2plus2[rk+(board[i]+(52-CARDS)+1)];
    }

    // 获取排除当前board的牌总和
    constexpr int except_board_deck_size = Poker_t::deck_len-Poker_t::hand_len[Poker_t::num_rounds-1];
    std::vector<type::card_t> except_board_deck;
    except_board_deck.reserve(except_board_deck_size);
    std::remove_copy_if(Poker_t::deck.begin(), Poker_t::deck.end(), std::inserter(except_board_deck, except_board_deck.begin()),
        [hole, board](type::card_t val){
            auto eq = [&val](type::card_t v){
                return val == v;
            };
            return std::any_of(hole, hole + Poker_t::hole_len[Poker_t::num_rounds-1], eq) || std::any_of(board, board + Poker_t::board_len[Poker_t::num_rounds-1], eq);
        });
    assert(except_board_deck.size() == except_board_deck_size);

    // 获取自己的rank
    int self_rank = rk;
    for (int i = 0, showdown_hlen = Poker_t::hole_len[Poker_t::num_rounds-1]; i < showdown_hlen; ++i){
        self_rank = Evaluator<Numeral211>::lookup_2plus2[self_rank+(hole[i]+(52-CARDS)+1)];
    }
    self_rank = Evaluator<Numeral211>::lookup_2plus2[self_rank];

    // 遍历组合生成直方图
    combinatorics::for_combinations([rk, self_rank, &showdown_histogram](auto begin, auto end){
            assert(end-begin == Poker_t::hole_len[Poker_t::num_rounds-1]);

            // 获取对手rank
            int opponent_rank = rk;
            for (auto it = begin; it != end; ++it){
                opponent_rank = Evaluator<Numeral211>::lookup_2plus2[opponent_rank+(*it+(52-CARDS)+1)];
            }
            opponent_rank = Evaluator<Numeral211>::lookup_2plus2[opponent_rank];

            //进行比较
            if(self_rank < opponent_rank)
                ++showdown_histogram[0];
            else if(self_rank > opponent_rank)
                ++showdown_histogram[2];
            else
                ++showdown_histogram[1];
        }
        , except_board_deck.begin(), except_board_deck.end(), Poker_t::hole_len[Poker_t::num_rounds-1]
    );

    return showdown_histogram;
}