import java.io.*;
import java.net.*;
import java.util.*;

public class Player extends Thread {
	private Connection conn = null;
	private String name = "";
	private FortSrv server = null;

	private boolean isconnected = false;
	private boolean isplaying = false;
	
	private ArrayList<District> hand;
	private ArrayList<District> city;
	private int gold;
		
	private String checkConnect( String s ) {
		String playername = null;
		
		if (s != null) {
			if (s.startsWith( "connect fortresses" )) {
				int major = server.majversion;
				int minor = server.minversion;
				int rmajor;
				int rminor;
				String rname;

				MyStringTokenizer st = new MyStringTokenizer( s );
				st.nextToken();
				st.nextToken();
				rmajor = Integer.parseInt( st.nextToken() );
				rminor = Integer.parseInt( st.nextToken() );
				st.nextToken();
				rname = (st.rest()).replaceAll( "\\W", "_" );

				if (rmajor >= major && rminor >= minor) {
					playername = rname;
				}
			}
		}		
		return playername;
	}
	
	public Player( Connection connection, FortSrv srv, int idx, Vector<District> startinghand ) {
		super( "Player " + idx );
		conn = connection;
		server = srv;
		
		hand = new ArrayList<District>( startinghand );
		city = new ArrayList<District>();
		gold = 2;
	}
	
	public void talk( String from, boolean broadcast, String message ) {
		conn.sendln( "talk:" + from + ":" + broadcast + ":" + message );
	}
	
	public void sendMessage( FMessage msg ) {
		conn.sendln( msg.toString() );
	}
	
	public void run() {
		String instr;
		boolean drop = false;
		String msgheader = "";
		
		try {
			conn.sendln( "Welcome to " + server.serverInfo() );
			instr = checkConnect( conn.readln() );
			if (instr == null) {
				System.out.println( getName() + " not using correct protocol. " +
				                    "Disconnecting." );
				drop = true;
			} else {
				System.out.println( "Player '" + getName() + "' connected." );
				conn.sendln( "Ok." );
				isconnected = true;
				try {
					String oldname = getName(); 
					setName( instr );
					System.out.println( "'" + oldname + "' changed name to '" +
					                    getName() + "'." );
				} catch (SecurityException e) {
					System.out.println( "Unable to change name '" + getName() +
					                    "' to '" + instr + "'" );
				}
				
				conn.sendln( getName() + " connected." );
				conn.sendln( "Send 'help' for help." );
				prompt();
			}
		} catch (IOException e) {
			drop = true;
			System.out.println( "Unable to establish connection with player.\n" +
			                    "Reason: " + e.getMessage() );
		}

		try {
			if (!drop) {
				while ((instr = conn.readln()) != null) {
					StringTokenizer st = new StringTokenizer( instr );
					msgheader = st.nextToken();
						
					if (msgheader.equals( "rename" )) {
					} else if (msgheader.equals( "say" ) ||
					           msgheader.equals( "shout" )) {
						FMessage msg = FMessage.createMessage( instr, getName() );
						if (! server.sendMessage( msg )) {
							if (! server.playerExists( msg.playerto )) {
								conn.sendln( "Could not send message. No such player " + msg.playerto + "." );
							} else {
								try {
									conn.sendln( "Could not send message to " +
									             server.getPlayerName( msg.playerto ) + "." );
								} catch (Exception e) {
									// syncro error? seems unlikely, but meh. not serious, anyway.
									conn.sendln( "Could not send message to player " + msg.playerto + "." );
								}
							}
						}
					} else if (msgheader.equals( "action" ) ||
					           msgheader.equals( "income" ) ||
							   msgheader.equals( "special" ) ||
							   msgheader.equals( "build" ) ||
							   msgheader.equals( "district" ) ||
							   msgheader.equals( "endturn" )) {
					} else if (msgheader.equals( "selectrole" ) ||
					           msgheader.equals( "discardrole" )) {
					} else if (msgheader.equals( "status" )) {
						if (st.hasMoreTokens()) {
							conn.sendln( server.playerStatus( Integer.parseInt( st.nextToken() ) ) );
						} else {
							conn.sendln( getExtendedStatus() );
						}
					} else if (msgheader.equals( "players" )) {
					} else if (msgheader.equals( "help" )) {
						if (st.hasMoreTokens()) {
							conn.sendln( server.protocolHelp( st.nextToken() ) );
						} else {
							conn.sendln( server.protocolHelp( "help" ) );
						}
					} else if (msgheader.equals( "serverinfo" )) {
						conn.sendln( server.serverInfo() );
					} else if (msgheader.equals( "q" ) ||
					           msgheader.equals( "quit" )) {
						drop = true;
						conn.sendln( "Goodbye." );
						System.out.println( getName() + " disconnected." );
					} else {
						conn.sendln( "Command '" + msgheader + "' not recognized." );
					}
					
					if (drop) {
						break;
					}
					prompt();
				}
			}
		} catch (IOException e) {
			System.out.println( "Connection error talking to '" + getName() + "'.\n" +
			                    "Reason: " + e.getMessage() );
		}

		conn.close();
	}
	
	public String getStatus() {
		String s = "Name: " + getName() + "\n" +
		           "Gold: " + getGold() + "\n" +
				   "Hand: (" + hand.size() + " cards)\n" +
				   "City:\n" + getDistrictDetails( city ) +
				   server.playerFlags( getIndex() );

		return s;
	}
	
	private String getDistrictDetails( ArrayList<District> list ) {
		StringBuilder s = new StringBuilder();
		
		if (list.isEmpty()) {
			s.append( "  (no cards)\n" );
		} else {
			for (District d : hand) {
				s.append( "  " + d.toString() + "\n" );
			}
		}
		
		return s.toString();
	}
	
	private String getExtendedStatus() {
		String s = "Player index: " + getIndex() + "\n" +
		           "Name: " + getName() + "\n" +
		           "Gold: " + getGold() + "\n" +
				   "Hand:\n" + getDistrictDetails( hand ) +
				   "City:\n" + getDistrictDetails( city ) +
				   server.playerFlags( getIndex() );

		return s;
	}
	
	public boolean connected() {
		return isconnected;
	}
	
	public int getGold() {
		return gold;
	}
	
	public int addGold( int g ) {
		gold += g;

		return gold;
	}
	
	public int removeGold( int g ) {
		gold -= g;
		
		return gold;
	}
	
	public int stealGold() {
		int g = gold;
		gold = 0;
		return g;
	}
	
	public int getIndex() {
		return server.getIndex( this );
	}
	
	public boolean playing() {
		return isplaying;
	}
	
	public synchronized void setPlaying( boolean p ) {
		isplaying = p;
	}
	
	private void prompt() {
		conn.send( "> " );
	}
}
