package mapping;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import logging.Logger;
import mapping.data.Server;

/**
 * @author Majd Kokaly
 * 
 * This server is responsible for receiving availability information from servers
 * (nodes) in the grid and updating the Server objects accordingly.
 * 
 */

public class AvailabilityServer implements Runnable {

	/**
	 * This flag tells the thread if it is alive or should kill itself.
	 */
	private boolean alive = true;

	/**
	 * The port that this servers listen on. By default the value is 37933.
	 */
	private int availablityPort = 37933;

	/**
	 * The Mapper object that contains the serversTable that this server
	 * modifies.
	 */
	private Mapper mapper;

	/** A logger object responsible for logging events */
	private Logger logger;

	/**
	 * A Thread objects that runs this Runnable class.
	 */
	private Thread thisThread;

	/**
	 * Default constructor.
	 * 
	 * @param mapper
	 *            to set the mapper field
	 */
	public AvailabilityServer(Mapper mapper) {
		this.setMapper(mapper);
		this.setLogger(new Logger("AvailabilityServer"));
	}

	public synchronized boolean isAlive() {
		return alive;
	}

	public synchronized void setAlive(boolean alive) {
		this.alive = alive;
	}

	public int getAvailablityPort() {
		return availablityPort;
	}

	public void setAvailablityPort(int availablityPort) {
		this.availablityPort = availablityPort;
	}

	public Mapper getMapper() {
		return mapper;
	}

	public void setMapper(Mapper mapper) {
		this.mapper = mapper;
	}

	public Logger getLogger() {
		return logger;
	}

	public void setLogger(Logger logger) {
		this.logger = logger;
	}

	/**
	 * This method starts the the thread.
	 */
	public void startServer() {
		this.setAlive(true);
		thisThread = new Thread(this);
		thisThread.start();
	}

	/**
	 * This method stops the the server and the thread.
	 */
	public void stopServer() {
		boolean wasAlive = this.isAlive();
		this.setAlive(false);
		if (wasAlive) {
			thisThread.interrupt();
			thisThread = null;
		}
	}

	/**
	 * The ServerSocket object used to listen to incoming connections.
	 */
	ServerSocket serverSocket = null;

	/**
	 * The Socket object used to handle connections.
	 */
	Socket socket = null;

	/**
	 * The input stream used to read data from the socket.
	 */
	DataInputStream in = null;

	/**
	 * In the run method, this thread listens on a port for aj values sent by
	 * the Servers and change the servers table accordingly. The format of the
	 * message received is serverAddress#availability
	 */
	public void run() {
		this.setAlive(true);
		String hostname = new String();
		String availability = new String();

		try {
			serverSocket = new ServerSocket(this.getAvailablityPort());
		} catch (IOException e1) {
			e1.printStackTrace();
		}

		while (this.isAlive()) {
			hostname = "";
			availability = "";
			try {
				System.out.println("Availability Server is waiting...");

				socket = serverSocket.accept();

				if (!this.isAlive())
					return;

				in = new DataInputStream(socket.getInputStream());
				System.out.println("Availability Server received a message: ");
				// Format of message sent is: serverAddress#availability
				int r;
				// Parsing server address
				while ((r = in.read()) != -1 && r != '#') { // # is the
					hostname = hostname + ((char) r);
				}
				// Parsing availability
				while ((r = in.read()) != -1 && r != '#') { // # is the
					availability = availability + (char) r;
				}
				this.getLogger().log("Availability received from (" + hostname + ") as (" + availability + ")");

				Server s = null;
				if (this.getMapper() != null)
					if (this.getMapper().getServersTable() != null)
						s = this.getMapper().getServersTable().getServer(hostname);
					else {
						System.err.println("ServersTable() not init");
					}
				else {
					System.err.println("Mapper not init");
				}
				if (s != null)
					s.setAvailabilty(Double.parseDouble(availability));
				else
					System.err.println("Server is not initialized");

			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				if (in != null)
					try {
						in.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				if (socket != null) {
					try {
						socket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

			}
		}
	}
}
