package net.mc_pandacraft.java.plugin.pandacraftutils.network_api;

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashMap;

import org.bukkit.ChatColor;

import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils;
import net.mc_pandacraft.java.plugin.pandacraftutils.network_api.RequestAnalyser.BadRequestException;
import net.mc_pandacraft.java.plugin.pandacraftutils.network_api.request_executors.AbstractRequestExecutor;

public class NetworkAPIListener extends Thread {
	

	private static NetworkAPIListener instance;
	
	/**
	 * Retourne l'unique instance de la classe. Si elle n'existe pas, on tente de la créer.<br/>
	 * Cette classe étant un enfant de Thread, la création de l'instance provoque son exécution
	 * @return L'unique instance de la classe
	 */
	
	public synchronized static NetworkAPIListener getInstance() {
		if (instance == null)
			loadNewInstance();
		return instance;
	}
	
	public synchronized static void loadNewInstance() {
		if (instance != null && instance.isAlive()) {
			instance.closeServerSocket(); // cet appel est censé provoquer l'arrêt du thread
			try { instance.join(); } catch (InterruptedException e) { }
		}
		instance = new NetworkAPIListener();
		instance.start();
	}
	
	
	
	
	
	
	
	private PandacraftUtils plugin = PandacraftUtils.getInstance();
	private int port = plugin.getServer().getPort()+9;
	private ServerSocket serverSocket;
	private HashMap<String, AbstractRequestExecutor> requestExecutors = new HashMap<String, AbstractRequestExecutor>();
	
	private NetworkAPIListener() {
		super("NetworkAPI Listener");
		plugin.getServer().getLogger().info("System property 'file.encoding' was set to 'UTF-8'");
		
		
	}
	
	@Override
	public void run() {
		synchronized (this) {
			try {
				serverSocket = new ServerSocket(port);
			} catch (IOException e) {
				plugin.getServer().getLogger().severe(e.getMessage());
				System.err.println(e.getMessage());
				return;
			}
		}
		
		plugin.getServer().getLogger().info(ChatColor.GREEN+"NetworkAPIListener à l'écoute sur le port "+port);
		
		int i=0;
		
		try {
			// réception des connexion client
			while (!serverSocket.isClosed()) {
				Socket socketClient = serverSocket.accept();
				new PacketExecutor(socketClient, i++).start();
			}
		} catch(IOException e) { }
		
		synchronized (this) {
			try {
				if (!serverSocket.isClosed())
					serverSocket.close();
			} catch (IOException e) { }
		}
		
		plugin.getServer().getLogger().info(ChatColor.GREEN+"NetworkAPIListener ferme le port "+port);
	}
	
	/**
	 * Ferme le ServerSocket. Ceci provoque l'arrêt du thread associé à l'instance de la classe
	 */
	private synchronized void closeServerSocket() {
		if (serverSocket != null)
		{
			try {
				serverSocket.close();
			} catch (IOException e) { }
		}
	}
	
	
	public int getPort() { return port; }
	
	
	
	
	public void registerRequestExecutor(String command, AbstractRequestExecutor executor) {
		requestExecutors.put(command, executor);
	}
	
	public AbstractRequestExecutor getRequestExecutor(String command) {
		return requestExecutors.get(command);
	}
	
	
	public String getCommandList() {
		return Arrays.toString(requestExecutors.keySet().toArray());
	}
	
}














/**
 * Prends en charge un socket client et le transmet au gestionnaire de paquet correspondant.<br/>
 * La connexion est fermée après chaque requête du client (règle pouvant évoluer)
 * @author Marc
 *
 */
class PacketExecutor extends Thread {
	private PandacraftUtils plugin = PandacraftUtils.getInstance();
	private Socket socket;
	
	public PacketExecutor(Socket s, int iteration) {
		super("NetworkAPI Input Packet #"+iteration);
		socket = s;
	}
	
	@Override
	public void run() {
		try {
			
			// analyse de la requête
			RequestAnalyser analyse = new RequestAnalyser(socket);
			
			
			AbstractRequestExecutor executor = NetworkAPIListener.getInstance().getRequestExecutor(analyse.command);
			
			executor.execute(analyse.data, socket);
			
			
			
		} catch (IOException e) {
			
			plugin.getServer().getLogger().severe("IOException "+e.getMessage());
		} catch (BadRequestException e) {
			Response rep = new Response();
			rep.good = false;
			rep.data = e.getMessage();
			try {
				rep.sendPacket(new PrintStream(socket.getOutputStream()));
			} catch (IOException e1) { }
			
			plugin.getServer().getLogger().severe("BadRequestException "+e.getMessage());
			
		} catch(Throwable e) {
			e.printStackTrace();
		}
		
		try {
			socket.close();
		} catch (Exception e) { }
	}
}