package com.server.controller;

import com.games.swarm.*;
import com.server.*;
import com.server.controller.*;
import com.common.*;
import java.util.*;

/**
 * SWARMController.java
 * 
 * Server-side game logic for SWARM game
 *
 * @author <a href="mailto:bret@hypefiend.com">bret barker</a>
 * @version 1.0
 */
public class SWARMController extends GameController {

    /** list of connected players */
    protected Hashtable players;

    /** list of games */
    protected HashMap games;

    /** possible moves */
    String moves[] = {"rock", "paper", "scissors" };
    
    /** 
     * game results
     * 0 = tie, 1 = player1, 2 = player2 
     **/
    int results[][] = { 
	{0,1,2},
	{2,0,1},
	{1,2,0}
    };
    
    /** text strings for results */
    String resultStrs[] = { "Tie!", "You Win", "Opponent Wins"};
    
    /**
     * return the gameName for this controller
     */
    public String getGameName() {
	return "SWARM";
    }

    /** 
     * do ChatController specific initialization here 
     */
    public void initController(GameConfig gc) {
	players = new Hashtable();
	//	clients = new HashMap();
	games = new HashMap();
    }

    /**
     * just use the default Player class
     */
    public Player createPlayer() {
	PlayerDefault p = new PlayerDefault();
	p.setSessionId(gameServer.nextSessionId());
	return p;
    }
   	
    /**
     * just use the default GameEvent class
     */
    public GameEvent createGameEvent() {
	return new GameEventDefault();
    }

    /** 
     * process events pulled form the queue
     */
    public void processEvent(GameEvent e) {
        System.out.print("SWARMController::ProcessEvent\n");
	switch (e.getType()) {
	case GameEventDefault.C_LOGIN:
	    login(e);
	    break;
	case GameEventDefault.C_LOGOUT:
	    logout(e);
	    break;
	case GameEventDefault.C_JOIN_GAME:
	    join(e);
	    break;
	case GameEventDefault.C_QUIT_GAME:
	    quit(e);
	    break;
	case GameEventDefault.C_CHAT_MSG:
	    chat(e);
	    break;
	case GameEventDefault.C_MOVE:
	    move(e);
	    break;
	case GameEventDefault.C_BEARING:
	    sendBearing(e);
	    break;
	case GameEventDefault.C_SPEED:
	    sendSpeed(e);
	    break;
	case GameEventDefault.C_RATE:
	    sendTurningRate(e);
	    break;
	case GameEventDefault.C_CONNECT:
	    procConnect(e);
	    break;
	case GameEventDefault.C_DISCONNECT:
	    procDisconnect(e);
	    break;
        case GameEventDefault.C_TEST:
            procTestMsg(e);
            break;
	case GameEventDefault.C_GET_PLAYERS:
	    getPlayers(e);
	    break;
	case GameEventDefault.C_GET_INIT_STATE:
	    getInitialState(e);
	    break;
	}
    }

    /**
     * handle login events
     */
    protected void login(GameEvent e) {
	String pid = e.getPlayerId();
	
	Player p = gameServer.getPlayerById(pid);
	if (p == null) {
	    return;
	}
	
	
	p.setLoggedIn(true);
	
	// send ACK to player
	GameEventDefault la = new GameEventDefault(GameEventDefault.S_LOGIN_ACK_OK);
	sendEvent(la, p);

	// tell everyone this player is here
	GameEventDefault sbl = new GameEventDefault(GameEventDefault.SB_LOGIN, p.getPlayerId());
	sendBroadcastEvent(sbl, players.values());

	// add to our list
	players.put(pid, p);

	// send player list
	getPlayers(e);

    }

    /**
     * handle logout events
     */
    protected void logout(GameEvent e) {
	String pid = e.getPlayerId();
	Player p = (Player) players.get(pid);
	
	// if in game, kill it first
	if (p.inGame()) {
	    quit(e);
	}

	// remove the player
	players.remove(pid);

	// send them a disconnect
	GameEventDefault dis = new GameEventDefault(GameEventDefault.S_DISCONNECT, "logged out");
	sendEvent(dis, p);

	// tell everyone else
	GameEventDefault sbl = new GameEventDefault(GameEventDefault.SB_LOGOUT, p.getPlayerId());
	sendBroadcastEvent(sbl, players.values());

    }

    /** 
     * initiate a game w/another player 
     * synchronized so we don't have concurrency problems with multiple
     * players starting games with the same opponent
     */
    protected synchronized void join(GameEvent e) {
	String p1_id = e.getPlayerId();
	String p2_id = e.getMessage();

	Player p1 = (Player) players.get(p1_id);

	if (p2_id.equals(p1_id)) {
	    GameEventDefault jf = new GameEventDefault(GameEventDefault.S_JOIN_GAME_ACK_FAIL);
	    jf.setMessage("sorry, can't play against yourself");
	    sendEvent(jf, p1);
	    return;
	}
		
	Player p2 = (Player) players.get(p2_id);

	if ((p1 == null) || (p2==null)) {
	    GameEventDefault jf = new GameEventDefault(GameEventDefault.S_JOIN_GAME_ACK_FAIL);
	    jf.setMessage("unknown player id");
	    sendEvent(jf, p1);
	    return;
	}
	if (p1.inGame()) {
	    GameEventDefault jf = new GameEventDefault(GameEventDefault.S_JOIN_GAME_ACK_FAIL);
	    jf.setMessage("sorry, you are already in a game");
	    sendEvent(jf, p1);
	    return;
	}
	if (p2.inGame()) {
	    GameEventDefault jf = new GameEventDefault(GameEventDefault.S_JOIN_GAME_ACK_FAIL);
	    jf.setMessage("sorry, that player is already in a game");
	    sendEvent(jf, p1);
	    return;
	}

	// create new game
	SWARMGame g = new SWARMGame(p1, p2);
	games.put("" + g.getGameId(), g);
	p1.setGameId(g.getGameId());
	p2.setGameId(g.getGameId());

	// let them know
	GameEventDefault jok = new GameEventDefault(GameEventDefault.S_JOIN_GAME_ACK_OK);
	jok.setMessage("Game started: " + p1.getPlayerId() + " vs. " + p2.getPlayerId());
	sendEvent(jok, p1);
	
	jok = new GameEventDefault(GameEventDefault.S_JOIN_GAME_ACK_OK);
	jok.setMessage("Game started: " + p1.getPlayerId() + " vs. " + p2.getPlayerId());
	sendEvent(jok, p2);

    }

    /**
     * handle quit events
     */
    protected void quit(GameEvent e) {
	String p1_id = e.getPlayerId();
	Player player = (Player) players.get(p1_id);
	SWARMGame g = (SWARMGame) games.get("" + player.getGameId());

	if (g == null) {
	    GameEventDefault jf = new GameEventDefault(GameEventDefault.S_JOIN_GAME_ACK_FAIL);
	    jf.setMessage("you are not in a game");
	    sendEvent(jf, player);
	    return;
	}

	Player p1 = g.getPlayer1();
	Player p2 = g.getPlayer2();
	p1.setInGame(false);
	p2.setInGame(false);
	p1.setGameId(g.getGameId());
	p2.setGameId(g.getGameId());
	games.remove("" + g.getGameId());

	// return the ack, and final game stats
	String msg1 = "GameOver, player " + player.getPlayerId() + " has quit.\n";
	String msg2 = "GameOver\n";

	String msgt = "Final tallies\n" + 
	    p1.getPlayerId() + " wins: " + g.getP1Wins() + "\n" +
	    p2.getPlayerId() + " wins: " + g.getP2Wins() + "\n" +
	    "ties: " + g.getTies() + "\n";
	GameEventDefault qe = new GameEventDefault(GameEventDefault.SB_PLAYER_QUIT, msg1 + msgt);
	sendEvent(qe, p1);
	qe = new GameEventDefault(GameEventDefault.SB_PLAYER_QUIT, msg2 + msgt);
	sendEvent(qe, p2);
    }

    /** 
     * handle chat events
     */
    protected void chat(GameEvent e) {
	e.setType(GameEventDefault.SB_CHAT_MSG);
	sendBroadcastEvent(e, players.values());
    }
    
    /**
     * handle move events
     */

    protected void sendBearing(GameEvent e)
    {
      System.out.print("Message from: " + e.getPlayerId() + "\n");
      gameServer.swarmTestServer.HandleBearingUpdate(e.getFlValue(), 
                                   e.getPlayerId()); 
    }
    protected void sendSpeed(GameEvent e)
    {
      System.out.print("Message from: " + e.getPlayerId() + "\n");
      gameServer.swarmTestServer.HandleSpeedUpdate(e.getFlValue(), 
                                   e.getPlayerId()); 
    }
    protected void sendTurningRate(GameEvent e)
    {
      System.out.print("Message from: " + e.getPlayerId() + "\n");
      gameServer.swarmTestServer.HandleTurningRateUpdate(e.getFlValue(), 
                                   e.getPlayerId()); 
    }
    protected void procConnect(GameEvent e)
    {
      System.out.print("Message from: " + e.getPlayerId() + "\n");
      gameServer.swarmTestServer.HandleConnect(e.getBoatType(), 
                                   e.getPlayerId()); 
    }
    protected void procDisconnect(GameEvent e)
    {
      System.out.print("Message from: " + e.getPlayerId() + "\n");
      gameServer.swarmTestServer.HandleDisconnect( e.getPlayerId()); 
    }




    protected void procTestMsg(GameEvent e)
    {
      int count = e.getDataCount();
      int []data = new int[count];
      data = e.getData();
      System.out.print ("Received Test Message: "+ count +"\n");
      for (int x = 0; x < count; x++)
      {
        System.out.print(data[x]+"\n");
      }

    }

    protected void move(GameEvent e) {
	String p1_id = e.getPlayerId();
	Player player = (Player) players.get(p1_id);
	SWARMGame g = (SWARMGame) games.get("" + player.getGameId());

	if (g==null) {
	    GameEventDefault mf = new GameEventDefault(GameEventDefault.S_MOVE_ACK_FAIL);
	    mf.setMessage("you are not in a game");
	    sendEvent(mf, player);
	    return;
	}
	Player p1 = g.getPlayer1();
	Player p2 = g.getPlayer2();

	// check for a valid move
	String move = e.getMessage();
	if (move.equals("r")) move = "rock";
	if (move.equals("p")) move = "paper";
	if (move.equals("s")) move = "scissors";
	int myMove = -1;
	for (int i=0; i<moves.length;i++) {
	    if (moves[i].equals(move)) 
		myMove = i;
	}

	if (myMove == -1) {
	    GameEventDefault mf = new GameEventDefault(GameEventDefault.S_MOVE_ACK_FAIL);
	    mf.setMessage("invalid move");
	    sendEvent(mf, player);
	    return;
	}

	if (player.equals(p1) && (g.getP1Move() == -1)) {
	    g.setP1Move(myMove);
	}
	else if (player.equals(p2) && (g.getP2Move() == -1)) {
	    g.setP2Move(myMove);
	}
	else {
	    GameEventDefault mf = new GameEventDefault(GameEventDefault.S_MOVE_ACK_FAIL);
	    mf.setMessage("already submitted move");
	    sendEvent(mf, player);
	    return;
	}


	// send ack
	GameEventDefault e2 = new GameEventDefault(GameEventDefault.S_MOVE_ACK_OK, "move ok");
	sendEvent(e2, player);

	if ((g.getP1Move() != -1) && (g.getP2Move() != -1)) {
	    // round is complete
	    int n = (player.equals(p1)) ? g.getP2Move() : g.getP1Move();

	    int result = results[n][myMove];
	    String resultStr = resultStrs[result];

	    if (result == 0) 
		g.incTies();
	    else if (player.equals(p1) ^ (result == 2))
		g.incP1Wins();
	    else 
		g.incP2Wins();
	    
	    g.resetGame();

	    String msg = "Opponent chooses " + moves[n] + "\n" + resultStr;
	    GameEventDefault e3 = new GameEventDefault(GameEventDefault.S_ROUND_COMPLETE, msg);
	    sendEvent(e3, player);

	    // reverse output for other player
	    result = results[myMove][n];
	    resultStr = resultStrs[result];
	    msg = "Opponent chooses " + moves[myMove] + "\n" + resultStr;
	    e3 = new GameEventDefault(GameEventDefault.S_ROUND_COMPLETE, msg);
	    sendEvent(e3, g.getOpponent(player.getPlayerId()));
	}
    }

    /**
     * handle get_player events
     */
    protected void getPlayers(GameEvent e) {
	String pid = e.getPlayerId();
	Player p = (Player) players.get(pid);

	StringBuffer sb = new StringBuffer();
	sb.append("players online:\n");
	Iterator i = players.values().iterator();
	while(i.hasNext()) {
	    Player p2 = (Player) i.next();
	    sb.append(p2.getPlayerId());
	    sb.append("\n");
	}

	GameEventDefault pl = new GameEventDefault(GameEventDefault.S_GET_PLAYERS, sb.toString());
	sendEvent(pl, p);
    }

    /**
     * handle get_init_state events
     */
    protected void getInitialState(GameEvent e) {
	String pid = e.getPlayerId();
	Player p = (Player) players.get(pid);
        System.out.print("Received getInitialState Req from game\n");

	StringBuffer sb = new StringBuffer();
	sb.append("players online:\n");
	Iterator i = players.values().iterator();
	while(i.hasNext()) {
	    Player p2 = (Player) i.next();
	    sb.append(p2.getPlayerId());
	    sb.append("\n");
	}

//	GameEventDefault pl = new GameEventDefault(GameEventDefault.S_UPDATE, sb.toString());
//	sendEvent(pl, p);
    }
    public void SendUpdate(GameEvent e, String _Id)
    {
      Player p = (Player) players.get(_Id);
      sendEvent(e, p);
    }



}
