from monopyly import *
from collections import defaultdict


class CashFlow(object):
    POSITION = "Posi"
    CASH = "Cash"
    MORTGAGEABLE = "Mtg1"
    MORTGAGEABLE2 = "Mtg2"
    SELLABLE = "Sell"
    OWNED = "nP"
    INCOME = "In"
    COST = "Out"
    ONE_ROLL = "1R"
    MAX_COST = "mR"
    HOUSE = "nH"

    RESERVE_MONEY = "RM"
    BALANCE_SHEET = "BS"

    def __init__(self, game_state, player):
        self.holder = player
        self.elem = defaultdict(lambda: defaultdict(int))
        self.refresh(game_state)

    def show(self, game_state):
        for ply in game_state.players:
            Logger.log("~{0}: Balance {1}, Resv {2}, House {3}".format(ply, self.elem[ply][self.BALANCE_SHEET], self.elem[ply][self.RESERVE_MONEY], self.elem[ply][self.HOUSE]))

    def refresh(self, game_state):
        square_info = [self._square_cost(game_state, i) for i in range(40)]

        for player in game_state.players:
            mortgageable, mortgageable2 = 0, 0
            sellable = 0
            money_in = 0
            money_out = 0
            owned = 0
            house = 0
            cost_list = []
            for i in range(40):
                square = game_state.board.squares[i]
                cost = square_info[i]
                if type(square) == Street or type(square) == Station or type(square) == Utility:
                    if square.owner is player:
                        if not square.is_mortgaged:
                            money_in += square_info[i]
                            if type(square) == Street:
                                if square.number_of_houses == 0:
                                    mortgageable += square.mortgage_value
                                else:
                                    sellable += int(square.number_of_houses * square.house_price / 2)
                                    mortgageable2 += square.mortgage_value
                                    house += square.number_of_houses
                            else:
                                mortgageable += square.mortgage_value
                        owned += 1
                        cost = 0
                    else:
                        money_out += square_info[i]
                cost_list.append(cost)
            repair = self._max_repairs(game_state, player)
            chance_cost = max(repair, 150)
            cost_list[7], cost_list[22], cost_list[36] = chance_cost, chance_cost, chance_cost

            position = player.state.square
            position1 = (position + 2) % 40
            position2 = position + 12
            flag = 0
            if position2 > 39:
                position2 -= 40
                flag = 1
            if flag == 0:
                one_roll = max(cost_list[position1:position2+1])
            else:
                one_roll = max(cost_list[position1:] + cost_list[:position2+1])

            max_cost = max(cost_list)

            self.elem[player][self.POSITION] = player.state.square
            self.elem[player][self.CASH] = player.state.cash
            self.elem[player][self.MORTGAGEABLE] = mortgageable
            self.elem[player][self.MORTGAGEABLE2] = mortgageable2
            self.elem[player][self.SELLABLE] = sellable
            self.elem[player][self.OWNED] = owned
            self.elem[player][self.INCOME] = money_in
            self.elem[player][self.COST] = money_out
            self.elem[player][self.ONE_ROLL] = one_roll
            self.elem[player][self.MAX_COST] = max_cost
            self.elem[player][self.HOUSE] = house

        for player in game_state.players:
            self.elem[player][self.RESERVE_MONEY] = self.elem[player][self.CASH] + self.elem[player][self.MORTGAGEABLE]
            self.elem[player][self.BALANCE_SHEET] = self.elem[player][self.INCOME] - self.elem[player][self.COST]

    def _square_cost(self, game_state, index):
        square = game_state.board.squares[index]
        cost = 0
        if square.name == Square.Name.INCOME_TAX:
            cost = 200
        elif square.name == Square.Name.SUPER_TAX:
            cost = 100
        elif type(square) == Street:
            owner = square.owner
            if owner is not None and not square.is_mortgaged:
                if square.number_of_houses == 0:
                    cost = square.rents[0]
                    if square.property_set in owner.state.owned_unmortgaged_sets:
                        cost *= 2
                else:
                    cost = square.rents[square.number_of_houses]
        elif type(square) == Station:
            owner = square.owner
            if owner is not None and not square.is_mortgaged:
                number_of_owned_stations = len(game_state.board.get_property_set(PropertySet.STATION).intersection(owner.state.properties))
                cost = 25 * number_of_owned_stations
        elif type(square) == Utility:
            owner = square.owner
            if owner is not None and not square.is_mortgaged:
                number_of_owned_utilities = len(game_state.board.get_property_set(PropertySet.UTILITY).intersection(owner.state.properties))
                if number_of_owned_utilities == 1:
                    cost = 28
                elif number_of_owned_utilities == 2:
                    cost = 70
        elif type(square) == CommunityChest:
            cost = 100
        return cost

    def _max_repairs(self, game_state, player):
        number_of_houses, number_of_hotels = player.state.get_number_of_houses_and_hotels(game_state.board)
        return 40 * number_of_houses + 115 * number_of_hotels

