"""
Strategies submitted to Axelrod's first tournament. All strategies in this
module are prefixed by `FirstBy` to indicate that they were submitted in
Axelrod's First tournament by the given author.

Note that these strategies are implemented from the descriptions presented
in:

Axelrod, R. (1980). Effective Choice in the Prisoner’s Dilemma.
Journal of Conflict Resolution, 24(1), 3–25.

These descriptions are not always clear and/or precise and when assumptions have
been made they are explained in the strategy docstrings.
"""

from typing import Dict, List, Optional, Tuple

from scipy.stats import chisquare

from axelrod.action import Action

from axelrod.player import Player

from axelrod.strategy_transformers import FinalTransformer

from .memoryone import MemoryOnePlayer

C, D = Action.C, Action.D

class FirstByGraaskamp(Player):
    """
    Submitted to Axelrod's first tournament by James Graaskamp.

    The description written in [Axelrod1980]_ is:

    > "This rule plays tit for tat for 50 moves, defects on move 51, and then
    > plays 5 more moves of tit for tat. A check is then made to see if the player
    > seems to be RANDOM, in which case it defects from then on. A check is also
    > made to see if the other is TIT FOR TAT, ANALOGY (a program from the
    > preliminary tournament), and its own twin, in which case it plays tit for
    > tat. Otherwise it randomly defects every 5 to 15 moves, hoping that enough
    > trust has been built up so that the other player will not notice these
    > defections.:

    This is implemented as:

    1. Plays Tit For Tat for the first 50 rounds;
    2. Defects on round 51;
    3. Plays 5 further rounds of Tit For Tat;
    4. A check is then made to see if the opponent is playing randomly in which
       case it defects for the rest of the game. This is implemented with a chi
       squared test.
    5. The strategy also checks to see if the opponent is playing Tit For Tat or
       a clone of itself. If
       so it plays Tit For Tat. If not it cooperates and randomly defects every 5
       to 15 moves.

    Note that there is no information about 'Analogy' available thus Step 5 is
    a "best possible" interpretation of the description in the paper.
    Furthermore the test for the clone is implemented as checking that both
    players have played the same moves for the entire game. This is unlikely to
    be the original approach but no further details are available.

    This strategy came 9th in Axelrod’s original tournament.

    Names:

    - Graaskamp: [Axelrod1980]_
    """

    name = "First by Graaskamp"
    classifier = {
        "memory_depth": float("inf"),
        "stochastic": True,
        "long_run_time": False,
        "inspects_source": False,
        "manipulates_source": False,
        "manipulates_state": False,
    }

    def __init__(self, alpha: float = 0.05) -> None:
        """
        Parameters
        ----------
        alpha: float
            The significant level of p-value from chi-squared test with
            alpha == 0.05 by default.
        """
        super().__init__()
        self.alpha = alpha
        self.opponent_is_random = False
        self.next_random_defection_turn = None  # type: Optional[int]

    def strategy(self, opponent: Player) -> Action:
        """This is the actual strategy"""
        # First move
        if not self.history:
            return C
        # React to the opponent's last move
        if len(self.history) < 56:
            if opponent.history[-1] == D or len(self.history) == 50:
                return D
            return C

        # Check if opponent plays randomly, if so, defect for the rest of the game
        p_value = chisquare([opponent.cooperations, opponent.defections]).pvalue
        self.opponent_is_random = (
            p_value >= self.alpha
        ) or self.opponent_is_random

        if self.opponent_is_random:
            return D
        if (
            all(
                opponent.history[i] == self.history[i - 1]
                for i in range(1, len(self.history))
            )
            or opponent.history == self.history
        ):
            # Check if opponent plays Tit for Tat or a clone of itself.
            if opponent.history[-1] == D:
                return D
            return C

        if self.next_random_defection_turn is None:
            self.next_random_defection_turn = self._random.randint(5, 15) + len(
                self.history
            )

        if len(self.history) == self.next_random_defection_turn:
            # resample the next defection turn
            self.next_random_defection_turn = self._random.randint(5, 15) + len(
                self.history
            )
            return D
        return C