package WheelOfFortune.game;

// Source file: U:\\cse308\\asn8\\src\\game\\GameModel.java
import java.io.*;
import WheelOfFortune.game.player.*;
import WheelOfFortune.game.wheel.*;
import WheelOfFortune.game.userOutput.*;
import WheelOfFortune.storage.*;
import java.util.*;

public class GameModel {
	public static final int SPIN = 11;
	public static final int BUY_VOWEL = 12;
	public static final int SOLVE = 13;
	
	public static final int VOWEL_PRICE = 250;
	
	public static final int WAITING_FOR_DECISION = 1;
	public static final int WAITING_FOR_CONSONANT = 2;
	public static final int WAITING_FOR_VOWEL = 3;
	public static final int WAITING_FOR_SOLUTION = 4;
	public static final int GAME_OVER = 5;
	
	private boolean hideUsedLetters = true;
	private boolean turboMode = false;
	private int state;
	private Timer t;
	private MainMenu mm;
	private GameOutput out;
	private boolean playSound;

	/**
	 * @return Returns the out.
	 */
	public GameOutput getOut() {
		return out;
	}
	private Wheel wheel;
	private Player players[];
	private int numPlayers;
	
	private LocalDatabase ld;
	private Board board;
	
	private int activePlayerIndex;
	
	private int round;
	
	private int numRounds;
	
	private OptionModel om;
	
	
	/**
	 * @roseuid 418933F201CE
	 */
	public GameModel(GameOutput out, OptionModel om, String wheelString, MainMenu mm) 
	throws FileNotFoundException, IOException, NoPlayerException {
		this.mm = mm;
		this.om = om;
		numPlayers=0;
		
		this.players = om.getP();
		for(int i = 0; i < 3; i ++)
		{
			if(players[i].getDifficulty() != 0)
				numPlayers++;
		}
		if(numPlayers == 0)
			throw new NoPlayerException();
		this.out = out;
		numPlayers = 3;
		wheel = om.getCurWheel();
		
		numRounds = om.getNumRounds();
		board = null;
		
		playSound = true;
		activePlayerIndex = 0;
		round = 0;

		
		//just use these players for now
		activePlayerIndex = 0;
		
		ld = new LocalDatabase("puzzles");
	}
	
	

	public synchronized void spinWheel() {
		if(state != WAITING_FOR_DECISION) return;
		out.println("Spinning wheel...");
		int sp = wheel.spin();
		out.getWP().spinWheel(sp);
		Space cs = wheel.getCurrentSpace();
		out.print("The wheel lands on: ");
		out.println(cs.getName());
		if(cs.getCode() == Space.NORMAL_SPACE) {
		 state = WAITING_FOR_CONSONANT;
		}
		else if(cs.getCode() == Space.LOSE_TURN) {
		 out.println("Sorry, " + activePlayer().getName() + " you lose your turn.");
		 nextPlayer();
		}
		else if(cs.getCode() == Space.BANKRUPT) {
		activePlayer().setRoundMoney(0);
		out.println("Sorry, " + activePlayer().getName() + " you have gone bankrupt.");
		 nextPlayer();
		}
		updateOutput();
		
	}
	
	//I should check for sufficient money here
	public synchronized void buyVowel() {
		state = WAITING_FOR_DECISION; 
		if(activePlayer().getRoundMoney() < VOWEL_PRICE) 
			out.println(activePlayer().getName() + ", you do not have enough money to buy a vowel."); 
		else state = WAITING_FOR_VOWEL;
		updateOutput();
		
	}
	
	
	public synchronized void guessConsonant(char c) {
		if(state != WAITING_FOR_CONSONANT) return;
		state = WAITING_FOR_DECISION;
		boolean result = helpGuessConsonant(c);
		if(playSound)
		{
			if(result)
				new SoundEffects().correctLetter();
			else
				new SoundEffects().incorrectLetter();
		}
		if(result == false) nextPlayer();
		else
		{
			if(board.solvePuzzle(board.display()))
			{
				guessSolution(board.display());
				return;
			}
			else 
			{
				if(activePlayer().isCPU())
					
				{ 	
					t = new Timer();
					
					if(!turboMode)
						t.schedule(new cpuTurn(),2000);
					else
						t.schedule(new cpuTurn(),0);
				}
				updateOutput();
			}
		}
		
	}
	private boolean helpGuessConsonant(char c) {
		
		if(Utilities.isConsonant(c) == false) {
			out.println("Sorry, " + c + " is not a consonant.");
			return false;
		}
		
		int occurences = board.guessLetter(c);
		if(occurences == -1) {
			out.println("Sorry, " + c + " has already been chosen.");
			return false;
		}
		
		if(occurences == 1) out.println("There is 1 " + c + ".");
		else out.println("There are " + occurences + " " + c + "\'s.");
		
		if(occurences > 0) {
			activePlayer().setRoundMoney(activePlayer().getRoundMoney() + activeSpace().getAmount() * occurences);
			return true;
		}
		
		return false;
	}
	
	public synchronized void guessVowel(char c) {
		if(state != WAITING_FOR_VOWEL) return;
		state = WAITING_FOR_DECISION;
		boolean result = helpGuessVowel(c);
		if(playSound)
		{
			if(result)
				new SoundEffects().correctLetter();
			else
				new SoundEffects().incorrectLetter();
		}
		if(result == false) nextPlayer();
		else
		{
			if(activePlayer().isCPU())
			{ 	
				t = new Timer();
				
				if(!turboMode)
					t.schedule(new cpuTurn(),2000);
				else
					t.schedule(new cpuTurn(),0);
			}
			updateOutput();
		}
		
	}
	//return true if their turn continues, false otherwise
	private boolean helpGuessVowel(char c) {
		activePlayer().setRoundMoney(activePlayer().getRoundMoney() - VOWEL_PRICE);
		
		if(Utilities.isVowel(c) == false) {
			out.println("Sorry, " + c + " is not a vowel.");
			return false;
		}
		
		int occurences = board.guessLetter(c);
		if(occurences == -1) {
			out.println("Sorry, " + c + " has already been chosen.");
			return false;
		}
		
		if(occurences == 1) out.println("There is 1 " + c + ".");
		else out.println("There are " + occurences + " " + c + "\'s.");
		if(occurences > 0) return true;
		return false;
	}
	
	public boolean guessSolution(String guess) {
		boolean result = helpGuessSolution(guess);
		
		//the player guessed right
		if(result == true) {
			endRound();
			state = GAME_OVER;
			updateOutput();
			ContinueTask ct = new ContinueTask();
			t = new Timer();
			t.schedule(ct,1000);
		}
		//the player guessed wrong
		else {
			if(playSound)
				new SoundEffects().loseTurn();
			nextPlayer();
		} 
		return result;
	}
	private boolean helpGuessSolution(String guess) {
		out.println(activePlayer().getName() + " guessed " + guess);
		boolean correct = board.solvePuzzle(guess);
		if(correct) {
			out.println("Congratulations, " + activePlayer().getName() + ", you have solved the puzzle.");
			return true;
		}
		out.println("Sorry that guess is incorrect.");
		return correct;
	}
	
	private class ContinueTask extends TimerTask 
	{
		
		public void run()
		{
			if((round + 1) < numRounds) {
				if(playSound)
					new SoundEffects().winRound();
				advanceRound();
				startNewRound();
				state = WAITING_FOR_DECISION;
				updateOutput();
				
			}
			else {
				state = GAME_OVER;
				updateOutput();
				int winner = max(players);
				if(playSound)
				{	
					if(!players[winner].isCPU())
						new SoundEffects().winGame();
					else
						new SoundEffects().loseGame();
				}
				new Timer().schedule(new TimerTask()
						{	public void run()
						{
							out.dispose();
							mm.show();
						}
						}, 2000);
			}
		}
	}
	public void setSound(boolean s)
	{
		playSound = s;
	}
	public int max(Player[] p)
	{
		int player = 0;
		int max = p[0].getTotalMoney();
		if(p[1].getTotalMoney() > max)
		{
			max = p[1].getTotalMoney();
			player = 1;
		}
		if(p[2].getTotalMoney() > max)
		{
			max = p[2].getTotalMoney();
			player = 2;
		}
		return player;
	}
	public void startGame()
	{
		out.show();
		startNewRound();
		updateOutput();
	}
	
	private void nextPlayer()
	{
		advanceActivePlayerIndex();
		while(activePlayer().getDifficulty() == 0)
			advanceActivePlayerIndex();
		state = WAITING_FOR_DECISION;
		updateOutput(); 	
		if(activePlayer().isCPU())
		{
			t = new Timer();
			t.schedule(new cpuTurn(),2000);
		}
	}
	/**
	 * @param code
	 * @roseuid 4189333B0060
	 */
	public synchronized void useToken(int code) {}
	
	
	
	
	public Player activePlayer() {
		return players[activePlayerIndex];
	}
	
	private Space activeSpace() {
		return wheel.getCurrentSpace();
	}
	
	private void advanceActivePlayerIndex() {
		if(activePlayer().getDifficulty() != 0)
			out.println(activePlayer().getName() + "'s turn ends.");
		activePlayerIndex = (activePlayerIndex + 1)%numPlayers;
		if(activePlayer().getDifficulty() != 0)
			out.println("It is now " + activePlayer().getName() + "'s turn.");
	}
	
	public void updateOutput() {
		out.updatePanels(this);
	}
	
	private void advanceRound() {
		round = round + 1;
	}
	
	private void startNewRound() {
		out.println("\nStarting Round " + (round + 1) + ".");
		Puzzle p;
		/*if(round == 0) p = new Puzzle("Software Engineer","Person");
		else if(round == 1) p = new Puzzle("Salt Lake City","Place");
		else p = new Puzzle("Investment Banker","Occupation");*/

		p = ld.getRandomPuzzle();
		
		board = new Board(p);
		out.getBP().setup(this);
		for(int i = 0;i < numPlayers;++i) players[i].setRoundMoney(0);
		state = WAITING_FOR_DECISION;
		if(activePlayer().isCPU())
		{
			t = new Timer();
			t.schedule(new cpuTurn(),2000);
		}
		if(activePlayer().getDifficulty()==0)
			nextPlayer();
	}
	
	private synchronized void cpuTakeTurn()
	{
		int decision = activePlayer().getStrategy().getDecision(this);
		if(decision == SPIN)
			cpuSpin();
		else if(decision == BUY_VOWEL)
			cpuBuy();
		else if(decision == SOLVE)
			cpuSolvePuzzle();
	}
	
	private synchronized void cpuSpin()
	{
		spinWheel();
		t = new Timer();
		if(!turboMode)
			t.schedule(new cpuTurn(),2000);
		else
			t.schedule(new cpuTurn(),0);
	}
	private synchronized void cpuGuessConsonant()
	{
		guessConsonant(activePlayer().getStrategy().getConsonant(this));
	}
	private synchronized void cpuBuy()
	{
		this.buyVowel();
		t = new Timer();
		if(!turboMode)
			t.schedule(new cpuTurn(),1000);
		else
			t.schedule(new cpuTurn(),0);
	}
	private synchronized void cpuGuessVowel()
	{
		this.guessVowel(activePlayer().getStrategy().getVowel(this));
		
	}
	private synchronized void cpuSolvePuzzle()
	{
		this.guessSolution(activePlayer().getStrategy().getPuzzleAnswer(this));
	}
	private class cpuTurn extends TimerTask
	{
		public synchronized void run()
		{
			if(!activePlayer().isCPU())
				return;
			if(state == WAITING_FOR_CONSONANT)
				cpuGuessConsonant();
			else if(state == WAITING_FOR_VOWEL)
				cpuGuessVowel();
			else if(state == WAITING_FOR_DECISION)
				cpuTakeTurn();
		}
	}
	
	
	private void endRound() {
		activePlayer().setTotalMoney(activePlayer().getTotalMoney() + activePlayer().getRoundMoney());
		out.println("\nTotal Money Standings");
		for(int i = 0;i < numPlayers;++i) out.println(players[i].getName() + ": $" + players[i].getTotalMoney());
	}
	
	
	
	
	
	
	public int getState() { return state; }
	
	
	/**
	 * @return int
	 * @roseuid 4189334002DE
	 */
	public int getActivePlayerIndex() {
		return activePlayerIndex;
	}
	
	
	/**
	 * @return Board
	 * @roseuid 4189334A021B
	 */
	public Board getBoard() {
		return board;
	}
	
	/**
	 * @param i
	 * @return Player
	 * @roseuid 41893350036E
	 */
	public Player getPlayer(int index) {
		return players[index];
	}
	
	/**
	 * @return int
	 * @roseuid 41893355022B
	 */
	public int getNumPlayers() {
		return numPlayers;
	}
	
	/**
	 * @return Wheel
	 * @roseuid 4189335A014C
	 */
	public Wheel getWheel() {
		return wheel;
	}
	/**
	 * @return Returns the hideUsedLetters.
	 */
	public boolean getHideUsedLetters() {
		return hideUsedLetters;
	}
	/**
	 * @param hideUsedLetters The hideUsedLetters to set.
	 */
	public void setHideUsedLetters(boolean hideUsedLetters) {
		this.hideUsedLetters = hideUsedLetters;
	}
}
