/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package rsx;

import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONException;
import org.json.JSONObject;
import rsx.tcp.TcpClient;
import rsx.udp.Broadcast;

/**
 * Connexion à un server BomberStudent
 *
 * @author Arthur Brandao
 */
public class BomberStudentClient {

    /**
     * Port pour le boradcast UDP
     */
    protected int portUdp;
    
    /**
     * Port pour la socket TCP
     */
    protected int portTcp;
    
    /**
     * Class pour broadcast
     */
    protected Broadcast finder;
    
    /**
     * Class pour le client TCP
     */
    protected TcpClient socket;
    
    /**
     * Class de gestion des requetes serveurs
     */
    protected BomberStudentRequest request;
    
    /**
     * Liste des handlers pour gerer les requetes du serveur
     */
    protected HashMap<String, BomberStudentHandler> handlers = new HashMap<>();

    /* --- Constructeurs --- */
    /**
     * Creation d'un client BomberStudent
     * Le port 18624 est utilisé pour le broadcast udp
     * Le port 18642 est utilisé pour la socket TCP
     */
    public BomberStudentClient() {
        this.portUdp = 18624;
        this.portTcp = 18642;
        this.finder = new Broadcast(this.portUdp);
    }

    /**
     * Creation d'un client BomberStudent
     * @param portUdp Le port pour le broadcast UDP
     * @param portTcp Le port pour la socket TCP
     */
    public BomberStudentClient(int portUdp, int portTcp) {
        this.portUdp = portUdp;
        this.portTcp = portTcp;
        this.finder = new Broadcast(this.portUdp);
    }

    /* --- Methodes --- */
    /**
     * Ajoute un handler
     * @param ressource Le nom de la ressource associè au handler
     * @param handler Le handler
     */
    public void addHandler(String ressource, BomberStudentHandler handler){
        this.handlers.put(ressource, handler);
    }
    
    /**
     * Cherche un serveur par broadcast
     * @return Le nombre de serveur trouvé
     */
    public int findServer() {
        this.finder.search("i'm a bomberstudent server");
        return this.finder.getServers().size();
    }

    /**
     * Selectionne un serveur pour s'y connecter
     * @param index Le numero du serveur dans la liste
     * @return Reussite
     */
    public boolean selectServer(int index) {
        ArrayList<InetAddress> serv = this.finder.getServers();
        System.out.println(serv.size());
        if (!(index >= 0 && index < serv.size())) {
            System.err.println("Index invalide");
            return false;
        }
        //Lancement gestionnaire des requetes serveur
        try {
            this.request = new BomberStudentRequest(serv.get(index), this.portTcp + 1, handlers);
            this.request.start();
        } catch (IOException ex) {
            return false;
        }
        //Creation socket d'envoi
        this.socket = new TcpClient(serv.get(index), this.portTcp);
        return this.socket.connect();
    }

    /**
     * Envoi requete sur le serveur
     * @param method Methode d'envoi (POST ou GET)
     * @param ressource Ressource demandée
     * @return Reussite
     */
    public boolean send(String method, String ressource) {
        return this.send(method, ressource, null);
    }

    /**
     * Envoi requete sur le serveur
     * @param method Methode d'envoi (POST ou GET)
     * @param ressource Ressource demandée
     * @param param Parametre JSON à envoyer au serveur
     * @return Reussite
     */
    public boolean send(String method, String ressource, JSONObject param) {
        //Verif que la methode existe
        method = method.toUpperCase();
        if (!method.equals("POST") && !method.equals("GET")) {
            return false;
        }
        //Creation json pour envoi
        JSONObject json = new JSONObject();
        json.put("methode", method);
        json.put("ressource", ressource);
        if (param != null) {
            json.put("param", param);
        }
        //Envoi
        this.socket.send(json.toString());
        return true;
    }

    /**
     * Attend une reponse du serveur
     * @return La reponse analysée
     */
    public JSONObject receive() {
        String msg = this.socket.receive();
        try {
            return new JSONObject(msg);
        } catch(JSONException ex){
            System.err.println("La reponse n'est pas en JSON : " + ex.getMessage());
            return null;
        }
    }

    /**
     * Ferme la connexion
     * @return Reussite
     */
    public boolean close() {
        try {
            //Laisse le temps au serveur de voir les messages
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
            
        }
        //Avertit le serveur
        this.send("POST", "client/end");
        //Coupe le thread
        this.request.interrupt();
        //Verif que le thread est bien arréte
        try {
            Thread.sleep(3000);
            if (this.request.isAlive()) {
                System.err.println("Imposible de tuer le thread de gestion des requetes");
            }
        } catch (InterruptedException ex) {

        }
        return this.socket.close();
    }

}
