package gwbot;

import gwbot.bot.GenericBot;
import gwbot.bot.impl.GoldwipfBot;
import gwbot.bot.impl.NicoBot;
import gwbot.gson.MessageWrapperExclStrat;
import gwbot.message.CarPositionsMessage;
import gwbot.message.GameEndMessage;
import gwbot.message.GameInitMessage;
import gwbot.message.GameStartMessage;
import gwbot.message.JoinMessage;
import gwbot.message.Message;
import gwbot.message.MessageWrapper;
import gwbot.message.PingMessage;
import gwbot.message.TurboAvailableMessage;
import gwbot.message.YourCarMessage;
import gwbot.track.Piece;
import gwbot.track.Track;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

/**
 *
 * @author Goldwipf <goldwipf@beheh.de>
 */
public final class Main {

	private static Socket socket;

	public static void main(String... args) {

		System.out.println("This is Team Goldwipf, ready to go");

		String host = args[0];
		int port = Integer.parseInt(args[1]);
		String botName = args[2];
		String botKey = args[3];

		System.out.print("Connecting to " + host + ":" + port + " as " + botName + "/" + botKey + "...");

		try {
			socket = new Socket(host, port);
			System.out.println(" connected");

			final PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
			final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));

			new Main(reader, writer, new JoinMessage(botName, botKey));
		}
		catch (IOException ex) {
			System.out.println("failed");
			ex.printStackTrace(System.err);
			System.exit(1);
		}
	}

	public final Gson gson;
	private final PrintWriter writer;

	public Main(final BufferedReader reader, final PrintWriter writer, final JoinMessage join) throws IOException {

		gson = new GsonBuilder().setExclusionStrategies(new MessageWrapperExclStrat()).create();

		this.writer = writer;
		String line = null;

		GenericBot bot = null;
		switch (System.getProperty("user.name")) {
			case "Nico Smeenk":
				bot = new NicoBot(this);
				break;
			default:
				bot = new GoldwipfBot(this);
				break;
		}
		System.out.println("Using " + bot.getClass().getSimpleName() + " as backend");

		// send join message
		System.out.print("Joining game...");
		send(join);

		// success, if we receive answer
		while ((line = reader.readLine()) != null) {
			final MessageWrapper msgFromServer = gson.fromJson(line, MessageWrapper.class);

			if (msgFromServer.msgType.equals("join")) {
				System.out.println(" accepted");
				continue;
			}

			boolean disconnect = false;
			switch (msgFromServer.msgType) {
				case "yourCar":
					// receive details about your car
					YourCarMessage yourCarMessage = gson.fromJson(msgFromServer.data.toString(), YourCarMessage.class);
					System.out.print("Receiving car data...");
					System.out.println(" \"" + yourCarMessage.getName() + "\", color is " + yourCarMessage.getColor());
					bot.onYourCarMessage(yourCarMessage);
					break;
				case "gameInit":
					// receive initial details about the following game
					GameInitMessage gameInitMessage = gson.fromJson(msgFromServer.data.toString(), GameInitMessage.class);
					Track track = gameInitMessage.getRace().getTrack();
					List<Piece> pieces = track.getPieces();
					System.out.print("Receiving track data...");
					for (int i = 0; i < pieces.size(); i++) {
						Piece piece = pieces.get(i);
						piece.setNext(pieces.get((i + 1 + pieces.size()) % pieces.size()));
						piece.setPrevious(pieces.get((i - 1 + pieces.size()) % pieces.size()));
					}
					/*for (Piece piece : pieces) {
					 System.out.println(piece);
					 }*/
					System.out.println(" track is \"" + track.getName() + "\" with " + track.getLanes().size() + " lanes");
					bot.onGameInitMessage(gameInitMessage);
					break;
				case "gameStart":
					// start the current game with details specified in gameInit
					GameStartMessage gameStartMessage = new GameStartMessage();
					System.out.print("Starting game!");
					bot.onGameStartMessage(gameStartMessage);
					break;
				case "carPositions":
					// receive
					Type carPositionsCollectionType = new TypeToken<ArrayList<CarPositionsMessage>>() {
					}.getType();
					List<CarPositionsMessage> carPositions = gson.fromJson(msgFromServer.data.toString(), carPositionsCollectionType);
					bot.onCarPositions(carPositions);
					break;
				case "turboAvailable":
					// turbo is available for a certain length
					TurboAvailableMessage turboAvailableMessage = gson.fromJson(msgFromServer.data.toString(), TurboAvailableMessage.class);
					System.out.println("Turbo is available.");
					bot.onTurboAvailable(turboAvailableMessage);
					break;
				case "lapFinished":
					// completed a lap
					// @todo
					break;
				case "crash":
					// crashing
					System.out.println("Crashed.");
					// @todo
					break;
				case "spawn":
					// respawn after crashing
					System.out.println("Respawned after crash.");
					// @todo
					break;
				case "gameEnd":
					// current game has ended
					GameEndMessage gameEndMessage = gson.fromJson(msgFromServer.data.toString(), GameEndMessage.class);
					System.out.println("Game has ended.");
					bot.onGameEndMessage(gameEndMessage);
					break;
				case "tournamentEnd":
					disconnect = true;
					// @todo
					System.out.println("Tournament has ended.");					
					break;
				case "error":
					System.err.println("Received error: " + msgFromServer.data.toString());
					System.err.println("Last message was: " + lastMessage.toJson(gson));
					break;
				default:
					// do nothing, return ping to acknowledge
					send(new PingMessage());
					System.out.println("Unknown message received: *" + msgFromServer.msgType);
					break;
			}

			if (disconnect) {
				break;
			}
		}

		System.out.println("Disconnecting from server");
		socket.close();
	}

	private Message lastMessage;

	public void send(final Message message) {
		lastMessage = message;
		writer.println(message.toJson(gson));
		writer.flush();
	}
}

@@ -4241,16 +4241,18 @@
 ut.print
+ln
 (%22Starti

print starting game message on new line