#!/usr/bin/env python
"""
Uses the Random Forest classifier

"""

from api import State, util
import random, os

from sklearn.externals import joblib

DEFAULT_MODEL = os.path.dirname(os.path.realpath(__file__)) + '/model.pkl'


class Bot:
    __max_depth = -1
    __randomize = True

    __model = None

    def __init__(self, randomize=True, depth=12, model_file=DEFAULT_MODEL):

        print(model_file)
        self.__randomize = randomize
        self.__max_depth = depth

        # Load the model
        self.__model = joblib.load(model_file)

    def get_move(self, state):

        val, move = self.value(state)

        return move

    def value(self, state, alpha=float('-inf'), beta=float('inf'), depth=0):
        """
        Return the value of this state and the associated move
        :param state:
        :param alpha: The highest score that the maximizing player can guarantee given current knowledge
        :param beta: The lowest score that the minimizing player can guarantee given current knowledge
        :param depth: How deep we are in the tree
        :return: val, move: the value of the state, and the best move.
        """
        if state.finished():
            return (1.0, None) if state.winner() == 1 else (-1.0, None)

        if depth == self.__max_depth:
            return self.heuristic(state), None

        best_value = float('-inf') if maximizing(state) else float('inf')
        best_move = None

        moves = state.moves()

        if self.__randomize:
            random.shuffle(moves)

        for move in moves:

            next_state = state.next(move)
            value, m = self.value(next_state, alpha, beta, depth + 1)

            if maximizing(state):
                if value > best_value:
                    best_value = value
                    best_move = move
                    alpha = best_value
            else:
                if value < best_value:
                    best_value = value
                    best_move = move
                    beta = best_value

            # Prune the search tree
            # We know this state will never be chosen, so we stop evaluating its children
            if alpha < beta:
                break

        return best_value, best_move

    def heuristic(self, state):
        # Convert the state to a feature vector
        feature_vector = [features(state)]

        # These are the classes: ('won', 'lost')
        classes = list(self.__model.classes_)

        # Ask the model for a prediction
        # This returns a probability for each class
        prob = self.__model.predict_proba(feature_vector)[0]
        # print prob
        # print('{} {} {}'.format(classes, prob, util.ratio_ships(state, 1)))

        # Weigh the win/loss outcomes (-1 and 1) by their probabilities
        res = -1.0 * prob[classes.index('lost')] + 1.0 * prob[classes.index('won')]

        return res


def maximizing(state):
    """
    Whether we're the maximizing player (1) or the minimizing player (2).
    :param state:
    :return:
    """
    return state.whose_turn() == 1


def features(state):
    # type: (State) -> tuple[float, ...]
    """
    Extract features from this state. Remember that every feature vector returned should have the same length.

    :param state: A state to be converted to a feature vector
    :return: A tuple of floats: a feature vector representing this state.
    """

    my_id = state.whose_turn()
    opponent_id = 1 if my_id == 0 else 0

    # How many ships does p1 have in garrisons?
    p1_garrisons = 0.0
    # How many ships does p2 have in garrisons?
    p2_garrisons = 0.0

    p1_planets = 0
    p2_planets = 0

    for planet in state.planets(my_id):
        p1_garrisons += state.garrison(planet)
        p1_planets += 1

    for planet in state.planets(opponent_id):
        p2_garrisons += state.garrison(planet)
        p2_planets += 1

    # How many ships does p1 have in fleets?
    p1_fleets = 0.0
    # How many ships does p2 have in fleets?
    p2_fleets = 0.0

    for fleet in state.fleets():
        if fleet.owner() == my_id:
            p1_fleets = fleet.size()
        else:
            p2_fleets += fleet.size()

    return p1_garrisons, p2_garrisons, p1_fleets, p2_fleets, p1_planets, p2_planets
