import numpy as np
from copy import deepcopy


class NormalFormGame():

    def __init__(self, game_matrix):
        self.game_matrix, self.game_matrix_as_array = self.set_game_matrix(game_matrix)
        self.n_outcomes = np.prod(np.array(game_matrix).shape[:-1])
        self.n_players = np.array(game_matrix).shape[-1]
        self.action_history = []
        self.payoffs_history = []

    def __str__(self):
        return 'Normal form game (base class)'

    def get_name(self):
        return self.__str__()

    def get_game_matrix(self):
        return self.game_matrix

    def get_game_matrix_as_array(self):
        return self.game_matrix_as_array

    def set_game_matrix(self, new_game_matrix):
        the_game_matrix = deepcopy(new_game_matrix)
        the_game_matrix_as_array = np.array(deepcopy(new_game_matrix))
        self.game_matrix, self.game_matrix_as_array = the_game_matrix, the_game_matrix_as_array
        return the_game_matrix, the_game_matrix_as_array

    def get_n_outcomes(self):
        return self.n_outcomes

    def get_n_players(self):
        return self.n_players

    def update_history(self, action_profile):
        assert len(action_profile) == self.n_players
        self.action_history.append(deepcopy(action_profile))

    def reset_history(self):
        self.action_history = []

    def get_history(self):
        return self.action_history

    def update_payoff_history(self, payoff_vect):
        assert len(payoff_vect) == self.n_players
        self.payoffs_history.append(deepcopy(payoff_vect))

    def reset_payoff_history(self):
        self.payoffs_history = []

    def get_payoff_history(self):
        return self.payoffs_history

    def simulate_game_round(self, action_profile):
        assert len(action_profile) == self.n_players
        for a, player_idx in zip(action_profile,range(self.n_players)):
            assert 0 <= a < self.game_matrix_as_array.shape[player_idx]
        payoff_vect = self.game_matrix_as_array[tuple(action_profile)].tolist()
        assert len(payoff_vect) == self.n_players
        return action_profile, payoff_vect

    def play_game_round(self, action_profile):
        actions, payoffs = self.simulate_game_round(action_profile)
        assert actions == action_profile
        self.update_history(actions)
        self.update_payoff_history(payoffs)
        return actions, payoffs
