"""
Strategies from Axelrod's second tournament. All strategies in this module are
prefixed by `SecondBy` to indicate that they were submitted in Axelrod's Second
tournament by the given author.
"""

from typing import List

import numpy as np

from axelrod.action import Action

from axelrod.interaction_utils import compute_final_score

from axelrod.player import Player

from axelrod.strategies.finite_state_machines import FSMPlayer

C, D = Action.C, Action.D

class SecondByWeiner(Player):
    """
    Strategy submitted to Axelrod's second tournament by Herb Weiner (K41R),
    and came in seventh in that tournament.

    Play Tit-for-Tat with a chance for forgiveness and a defective override.

    The chance for forgiveness happens only if `forgive_flag` is raised
    (flag discussed below).  If raised and `turn` is greater than `grudge`,
    then override Tit-for-Tat with Cooperation.  `grudge` is a variable that
    starts at 0 and increments 20 with each forgiven Defect (a Defect that is
    overriden through the forgiveness logic).  `forgive_flag` is lower whether
    logic is overriden or not.

    The variable `defect_padding` increments with each opponent Defect, but
    resets to zero with each opponent Cooperate (or `forgive_flag` lowering) so
    that it roughly counts Defects between Cooperates.  Whenever the opponent
    Cooperates, if `defect_padding` (before reseting) is odd, then we raise
    `forgive_flag` for next turn.

    Finally a defective override is assessed after forgiveness.  If five or
    more of the opponent's last twelve actions are Defects, then Defect.  This
    will overrule a forgiveness, but doesn't undo the lowering of
    `forgiveness_flag`.  Note that "last twelve actions" doesn't count the most
    recent action.  Actually the original code updates history after checking
    for defect override.

    Names:

    - Weiner: [Axelrod1980b]_
    """

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

    def __init__(self):
        super().__init__()
        self.forgive_flag = False
        self.grudge = 0
        self.defect_padding = 0
        self.last_twelve = [0] * 12
        self.lt_index = 0  # Circles around last_twelve

    def try_return(self, to_return):
        """
        We put the logic here to check for the defective override.
        """

        if np.sum(self.last_twelve) >= 5:
            return D
        return to_return

    def strategy(self, opponent: Player) -> Action:
        """Actual strategy definition that determines player's action."""
        if len(opponent.history) == 0:
            return C

        # Update history, lag 1.
        if len(opponent.history) >= 2:
            self.last_twelve[self.lt_index] = 0
            if opponent.history[-2] == D:
                self.last_twelve[self.lt_index] = 1
            self.lt_index = (self.lt_index + 1) % 12

        if self.forgive_flag:
            self.forgive_flag = False
            self.defect_padding = 0
            if (
                self.grudge < len(self.history) + 1
                and opponent.history[-1] == D
            ):
                # Then override
                self.grudge += 20
                return self.try_return(C)
            else:
                return self.try_return(opponent.history[-1])
        else:
            # See if forgive_flag should be raised
            if opponent.history[-1] == D:
                self.defect_padding += 1
            else:
                if self.defect_padding % 2 == 1:
                    self.forgive_flag = True
                self.defect_padding = 0

            return self.try_return(opponent.history[-1])