package com.beecavegames.poker.bot.strategies;

import java.util.Collection;
import java.util.Iterator;

import lombok.NoArgsConstructor;

import com.beecavegames.poker.PokerGame.Stage;
import com.beecavegames.poker.PokerSeat;
import com.beecavegames.poker.PokerTable;
import com.beecavegames.poker.PokerTablePlayer;
import com.beecavegames.poker.bot.PokerBot;
import com.beecavegames.poker.fsm.Event;
import com.beecavegames.util.MoneyAmount;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.SetMultimap;

@NoArgsConstructor
public class NeuralNetPlayStrategy implements PlayStrategy {

	/**
	 * 
	 */
	private static final long serialVersionUID = -813452212603846530L;

	@JsonIgnore
	public PokerNet net;
	
	public SetMultimap<Stage,Event> whatCouldHaveBeen=null;
	public boolean examiningRegret = false;
	
	public NeuralNetPlayStrategy(PokerNet n) {
		this.net=n;
	}

	public void setStructure(NeuralNetStructure structure) {
		net = new PokerNet(structure);
	}
	
	@Override
	public Event call(PokerBot bot, PokerSeat seat, PokerTable table) {
		Stage stage = table.getStage();
		if (examiningRegret) {
			Collection<Event> ot = whatCouldHaveBeen.get(stage);
			if (ot != null && !ot.isEmpty()) {
				Iterator<Event> i=ot.iterator();
				Event toExplore = i.next();
				i.remove();
				return toExplore;
			} 
		}		
		
		MoneyAmount amountToCall = MoneyAmount.max(table.getBigBlind(), table.bet);
		MoneyAmount amountToRaise = amountToCall.add(table
				.getBigBlind());

		PokerTablePlayer opponent = null;
		int seatNumber = bot.getSeatNumber();
		PokerSeat opponentSeat = null;
		for (PokerSeat s : table.getSeats()) {
			if (s.seatNumber != seatNumber) {
				if (opponent == null || s.bet.compareTo(opponentSeat.bet)>0) {
					opponentSeat = s;
					opponent = table
							.getPlayer(s.playerId);
				}
			}
		}

		MoneyAmount myStack = bot.getBalance();
		try {
			Event action = 
				net.decide(stage, 
					table.getActivePot().value.add(table.bettingRoundMinBet),
					amountToCall, table.bettingRoundMinBet,
					table.getBigBlind(), myStack, opponent.getBalance(),
					opponentSeat.bet, amountToRaise, bot.nm1Action,
					bot.lastBet, seat.getWinPercentage(), (double) opponent.bets
							/ opponent.folds);
			if ((action == Event.RAISE || action==Event.ALL_IN) && table.raisesCapped()) {
				action = Event.CALL;
			}
			if (whatCouldHaveBeen != null && !examiningRegret) {
				switch (action) {
				case ALL_IN: case RAISE:
					whatCouldHaveBeen.put(stage, Event.CALL);
					whatCouldHaveBeen.put(stage, Event.FOLD);
					break;
				case CALL:
					whatCouldHaveBeen.put(stage, Event.RAISE);
					whatCouldHaveBeen.put(stage, Event.FOLD);
					break;
				case FOLD:
					whatCouldHaveBeen.put(stage, Event.RAISE);
					whatCouldHaveBeen.put(stage, Event.CALL);
					break;
				}
			}
			return action;
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	@Override
	public void reset(boolean examiningRegret) {
		if (whatCouldHaveBeen != null && !examiningRegret)
			whatCouldHaveBeen.clear();
	}

	@Override
	public boolean hasOtherTimelines() {
		return whatCouldHaveBeen != null && !whatCouldHaveBeen.isEmpty();
	}

}
