#include "MasterMind.h"

MasterMind::MasterMind(int nb_colors, int nb_cases, std::default_random_engine& randomizer) :
    MasterMind(nb_colors, nb_cases, nullptr, randomizer) {
    m_solution = get_random_candidate();
    m_random_solution = true;
    m_vote = new int[nb_colors];
};

MasterMind::MasterMind(int nb_colors, int nb_cases, int* solution,  std::default_random_engine& randomizer) :
    m_nb_colors{nb_colors}, m_nb_cases{nb_cases}, m_solution{solution}, m_random_solution{false},
    m_randomizer{randomizer}, m_permutation{std::vector<int>(nb_cases)} {
    iota(m_permutation.begin(), m_permutation.end(), 0);
}

void MasterMind::set_new_random_solution() {
    if(m_random_solution)
        delete[] m_solution;
    m_solution = get_random_candidate();
    m_random_solution = true;
}

int* MasterMind::get_random_candidate() {
    int* cand = new int[m_nb_cases];
    for(int i=0; i<m_nb_cases; i++)
        cand[i] = (rand() % m_nb_colors);
    return cand;
}

void MasterMind::display_candidate(int* x) {
    std::cout << "[";
    for(int i=0; i<m_nb_cases; i++) {
        if(i) std::cout << ", ";
        std::cout << x[i];
    }
    std::cout << "]";
}

int MasterMind::try_candidate(int* candidate) {
    int total = 0;
    for(int i=0; i<m_nb_cases; i++)
        if(candidate[i] == m_solution[i])
            total++;
    return total;
}

int MasterMind::get_max_radius() {
    return m_nb_cases;
}

int* MasterMind::modify_with_radius(int* x, int r) {
    std::shuffle(begin(m_permutation), end(m_permutation), m_randomizer);

    std::uniform_int_distribution<int> unif(0,(m_nb_colors-1)-1);
    for(int i=0; i<r; i++) {
        int new_color = unif(m_randomizer);
        // to force the modification of color
        if(new_color == x[m_permutation[i]])
            new_color = m_nb_colors-1;
        x[m_permutation[i]] = new_color;
    }
    return x;
}

int* MasterMind::crossover(int* y, void* x, int nb, int* (*selector)(int, void*), int method) {
    std::uniform_int_distribution<int> unif(0,nb-1);
    switch(method) {
        case UNIFORM_CROSSOVER:
            for(int i=0; i<m_nb_cases; i++)
                y[i] = selector(unif(m_randomizer), x)[i];
            break;

        case MAJORITY_VOTE_CROSSOVER:
            for(int i=0; i<m_nb_cases; i++) {
                // Reset Vote
                for(int j=0; j<m_nb_colors; j++)
                    m_vote[j] = 0;
                // Proceed Vote
                int maxi = 0;
                int maxi_color = 0;
                for(int k=0; k<nb; k++) {
                    int color = selector(k, x)[i];
                    int val = ++m_vote[color];
                    if(val > maxi) {
                        maxi = val;
                        maxi_color = color;
                    }
                }
                // Use vote
                y[i] = maxi_color;
            }
            break;

        case K_POINT_CROSSOVER:
            break;
    }
}

void MasterMind::copy_candidate(int *x, int *y) {
    for(int i=0; i<m_nb_cases; i++)
        y[i] = x[i];
}

double MasterMind::get_progress(int* x, int v_x) {
    return (double) v_x / (m_nb_cases);
}

bool MasterMind::is_optimal(int* x, double objective) {
    int v_x = try_candidate(x);
    return is_optimal(x, v_x, objective);
}

bool MasterMind::is_optimal(int* x, int v_x, double objective) {
    return (v_x >= m_nb_cases*objective);
}

MasterMind::~MasterMind() {
    if(m_random_solution)
        delete[] m_solution;
    delete[] m_vote;
}
