package server;

import java.io.IOException;
import java.util.List;

import card.Card;
import player.BasePlayer;

public abstract class BaseGame {
	/*
	 * COLORS:
	 * GREEN
	 * RED
	 * YELLOW
	 * WHITE
	 * BLUE
	 * (COLORED)
	 */

//	public static final byte[] MAGIC = { 1, 3, 3, 7 };
//	public static final byte MOVE_DRY = 1 << 5;
//	public static final byte MOVE_UPDATE = 2;
//	public static final byte MOVE_WON = 3;
//	public static final byte MOVE_LOST = 4;
//	public static final byte MOVE_PLACE_CARD = 5;
//	public static final byte MOVE_TRASH_CARD = 6;
//	public static final byte MOVE_HINT = 7;
//	public static final byte META_FIRST_META = MOVE_DRY << 1;
//	public static final byte META_YOURTURN = META_FIRST_META + 0;
//	public static final byte META_READY = META_FIRST_META + 1;
//	public static final byte META_UNREADY = META_FIRST_META + 2;
//	public static final byte MOVE_INVALID = META_FIRST_META + 3;
//	public static final byte MOVE_VALID = META_FIRST_META + 4;
//
//	public static final byte HINT_COLOR = 0;
//	public static final byte HINT_VALUE = 1;

//	public static final byte CONNECTION_VALID = 8;
//	public static final byte CONNECTION_INVALID = 9;

	final static int GREEN = 0;
	final static int RED = 1;
	final static int YELLOW = 2;
	final static int WHITE = 3;
	final static int BLUE = 4;
	//final static int COLORED = 5;
	final static int COLORS = 5;
	final static int N_CARDS = 50;
	final static int MAX_HINTS = 8;
	final static int MAX_FLASHS = 3;

	int nPlayers = 0;
	static int CARDS_PER_PLAYER;
	int nCardsInDeck;

	int hints;
	int flashs;
	Card[][] playingDeck;
	int[] playingDeckCounter;
	List<Card> trash;
	BasePlayer[] players;
	int currentPlayer;
	
	//volatile boolean keepRunning;
	
	//int port;

	//List<String> history;
	List<Move> history;

	public BaseGame(){
		//keepRunning = true;
		Utils.init(System.err, Utils.VERBOSE, true);
		//init();
	}
	
	protected void init() {
		createPlayingDeck(); 
		createHistory();
		createTrash();
		CARDS_PER_PLAYER = nPlayers<=3 ? 5 : 4;
		hints = MAX_HINTS;
		flashs = 0;
		nCardsInDeck = N_CARDS;
	}

	protected abstract void createHistory();
	protected abstract void createTrash();

	protected void createPlayingDeck() {
		playingDeck = new Card[COLORS][5];
		playingDeckCounter = new int[5];
		for(int i=0; i<playingDeck.length; ++i) {
			for(int j=0; j<playingDeck[i].length; ++j) {
				playingDeck[i][j] = Card.DUMMY_CARD;
			}
		}
		for(int i=0; i<5; ++i) {
			playingDeckCounter[i] = 0;
		}
	}

	protected boolean hintAvailable() {
		return hints > 0;
	}

	protected void createPlayers() {
		players = new BasePlayer[nPlayers];
		for(int i=0; i<nPlayers; ++i) {
			players[i] = createPlayer(i, CARDS_PER_PLAYER, "Player " + i);
		}
	}

	protected BasePlayer createPlayer(int id, int nCards, String name) {
		return new BasePlayer(id, nCards, name);
	}

	protected BasePlayer createPlayer(Message m) {
		Utils.log(Utils.VERBOSE, "create BasePlayer");
		return new BasePlayer(m);
	}

	public BasePlayer getPlayer(int index) {
		return players[index];
	}

	/**
	 * Returns the state of the game at a given @param move index
	 * @param move the index of the move you want to get.
	 * Positive values are interpreted as index from the start (0 is the initial game).
	 * Negative values are interpreted as index from the end (-1 is the last state).
	 */
	public List<Move> getHistory(int move) {
		final int index = move>=0 ? move : history.size()+move;
		return history.subList(0, index);
	}

//	protected abstract void receive(DataInputStream inStream) throws Exception;
	protected abstract void processMsg(Message m);
	protected abstract boolean handleIOException(IOException ex);

//	public static void Log(String msg){
//		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
//		System.out.println("[" + sdf.format(new Date()) + "]: " + msg);
//	}

//	class CommunicationListener extends Thread {
//		//Socket socket;
//		//DataInputStream inStream;
//		private Communicator comm;
//		private volatile boolean keepRunning;
//		private final int TIMEOUT;
//
//		CommunicationListener(Communicator c, int timeout) {
//			comm = c;
//			keepRunning = true;
//			TIMEOUT = timeout;
//		}
//
//		void stop() { keepRunning = false; }
//
//		//SocketListenerThread(Socket socket){
//		//	this.socket = socket;
//		//	try {
//		//		inStream = new DataInputStream(socket.getInputStream());
//		//	} catch (IOException e) {
//		//		throw new RuntimeException(e); // TODO wer faengt das?
//		//	}
//		//}
//
//		@Override
//		public void run() {
//			//receive(inStream);
//			boolean retry = true;
//			do {
//				try {
//					comm.establish();
//					break;
//				} catch(IOException ex) {
//					retry = retryFailedIO(ex);
//				}
//			} while(retry);
//
//			try {
//				comm.establishConnection();
//			} catch(IOException ex) {
//				handleIOException(ex);
//				if(!keepRunning) return;
//			}
//			try {
//				comm.setTimeout(TIMEOUT);
//			} catch(IOException ex) {
//				handleIOException(ex);
//				if(!keepRunning) return;
//			}
//			try {
//				comm.waitUntilReady();
//			} catch(IOException ex) {
//				handleIOException(ex);
//				if(!keepRunning) return;
//			}
//			synchronized(comm) {
//				try {
//					comm.wait();
//				} catch(InterruptedException ex) { }
//			}
//			while(keepRunning) {
//				Move m = null;
//				try {
//					m = comm.receiveMove();
//				} catch(IOException ex) {
//					handleIOException(ex);
//					if(!keepRunning) return;
//				}
//				try {
//					processMove(m);
//				} catch(IOException ex) {
//					handleIOException(ex);
//				}
//			}
//		}
//	}
}
