package reseau;

import static reseau.Message.MessageAction.AUTHENTICATION;
import static reseau.Message.MessageAction.CREATE;
import static reseau.Message.MessageAction.DELETE;
import static reseau.Message.MessageAction.FETCH_BENCHMARKS;
import static reseau.Message.MessageAction.FETCH_GROUPS;
import static reseau.Message.MessageAction.FETCH_PROBLEMS;
import static reseau.Message.MessageAction.FETCH_RESULTS;
import static reseau.Message.MessageAction.FETCH_USERS;
import static reseau.Message.MessageAction.READ;
import static reseau.Message.MessageAction.SEND_FILES;
import static reseau.Message.MessageAction.UPDATE;
import static reseau.Message.MessageParameter.CURRENT_GROUP_ID;
import static reseau.Message.MessageParameter.CURRENT_USER_ID;
import static reseau.Message.MessageParameter.ID;
import static reseau.Message.MessageParameter.LOGIN;
import static reseau.Message.MessageParameter.METHOD_ID;
import static reseau.Message.MessageParameter.METHOD_IDS;
import static reseau.Message.MessageParameter.PASSWORD;
import static reseau.Message.MessageParameter.PROBLEM_ID;
import static reseau.Message.MessageParameter.TARGET_ID;
import static reseau.MessageAbstractFactory.FactoryType.BENCHMARK;
import static reseau.MessageAbstractFactory.FactoryType.GROUP;
import static reseau.MessageAbstractFactory.FactoryType.MANAGEMENT;
import static reseau.MessageAbstractFactory.FactoryType.PROBLEM;
import static reseau.MessageAbstractFactory.FactoryType.RESULT;
import static reseau.MessageAbstractFactory.FactoryType.USER;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.*;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

import client.model.benchmark.Benchmarks;
import client.model.group.Groupe;
import client.model.problem.Parameters;
import client.model.problem.Probleme;
import client.model.problem.Resultat;
import client.model.user.Utilisateur;

/**
 * User: Andréa
 * Date: 23/12/10
 * Time: 02:17
 */
public class OptimEISTIServerProxyFacade
        implements
        IDatabaseOperationsProxyFacade,
        IManagementOperationProxyFacade,
        IMathematicsOperationsProxyFacade {

    private static class OptimEISTIServerProxyFacadeHolder {
        private static final OptimEISTIServerProxyFacade instance = new OptimEISTIServerProxyFacade();
    }

    private OptimEISTIServerProxyFacade() {
    }

    public static OptimEISTIServerProxyFacade getInstance() {
        return OptimEISTIServerProxyFacadeHolder.instance;
    }

    @Override
    public Utilisateur createUser(int userid, Utilisateur template) throws UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        try {
            request = MessageAbstractFactory.getFactory(USER).createMessage(CREATE);

            request.addParameter(CURRENT_USER_ID, String.valueOf(userid));

            request.addBody(
                    XMLBuilder.BuildUserMessage(
                            template
                    )
            );
            response = JavaNetworkCommunication.sendMessage(request);
            
            System.out.println("id recup : "+response.extract(ID));

            template.setIdPersonne(
                    Integer.parseInt(response.extract(ID))
            );

            return template;
        } catch (DataNotFoundException e) {
            //HELP You shouldn't be able to reach this statement... Don't be silly
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Groupe createGroup(int userid, Groupe template) throws UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        try {
            request = MessageAbstractFactory.getFactory(GROUP).createMessage(CREATE);

            request.addParameter(CURRENT_USER_ID, String.valueOf(userid));

            request.addBody(
                    XMLBuilder.BuildGroupMessage(
                            template
                    )
            );
            response = JavaNetworkCommunication.sendMessage(request);

            template.setIdGroupe(
                    Integer.parseInt(response.extract(ID))
            );

            return template;
        } catch (DataNotFoundException e) {
            //HELP You shouldn't be able to reach this statement... Don't be silly
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Probleme createProblem(int userid, int groupid, Probleme template) throws UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        try {
            String domRepresentation = XMLBuilder.BuildProblemaMessage(
                    template
            );

            List filesNodeList = XPath.selectNodes(
                    new SAXBuilder().build(new StringReader(domRepresentation)),
                    "//chemin");

            char[] buffer = new char[2048];
            for (Object object : filesNodeList) {
                Element e = (Element) object;
                Message fileMessage = MessageAbstractFactory.getFactory(PROBLEM)
                        .createMessage(SEND_FILES);

                Reader fileReader = new FileReader(e.getValue());
                StringBuilder sb = new StringBuilder();
                while (fileReader.read(buffer) != -1)
                    sb.append(buffer);

                fileMessage.addBody(sb.toString());
                // HELP you can check message response here to check if files has been correctly transferred
                JavaNetworkCommunication.sendMessage(fileMessage);
            }

            request = MessageAbstractFactory.getFactory(PROBLEM).createMessage(CREATE);

            request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
            request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

            request.addBody(
                    domRepresentation
            );
            response = JavaNetworkCommunication.sendMessage(request);

            template.setIdProbleme(
                    Integer.parseInt(response.extract(ID))
            );

            return template;
        } catch (DataNotFoundException e) {
            //HELP You shouldn't be able to reach this statement... Don't be silly
            e.printStackTrace();
            return null;
        } catch (JDOMException e) {
            throw new NetworkException("Malformed problem : unable to parse content", e);
        } catch (IOException e) {
            throw new NetworkException("Malformed problem : unable to read content", e);
        }
    }

    @Override
    public Utilisateur readUser(int userid, int searchuserid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(USER).createMessage(READ);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));

        request.addParameter(TARGET_ID, String.valueOf(searchuserid));

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderUserMessage(response.getBody());
    }

    @Override
    public Groupe readGroupe(int searchedgroupid) throws DataNotFoundException {
        Message request = null;
        Message response = null;
        try {

            request = MessageAbstractFactory.getFactory(GROUP).createMessage(READ);

            request.addParameter(TARGET_ID, String.valueOf(searchedgroupid));
            response = JavaNetworkCommunication.sendMessage(request);

            return XMLReader.ReaderGroupMessage(response.getBody());
        } catch (UnauthorizedOperationException e) {
            //HELP You shouldn't be able to reach this statement... Don't be silly
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Probleme readProblem(int userid, int groupid, int searchedproblemid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(PROBLEM).createMessage(READ);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(searchedproblemid));

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderProblemaMessage(response.getBody());
    }

    @Override
    public Resultat readResult(int userid, int groupid, int searchedresultid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(RESULT).createMessage(READ);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(searchedresultid));

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderResultMessage(response.getBody());
    }

    @Override
    public Benchmarks readBenchmark(int userid, int groupid, int searchedbenchmarkid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(BENCHMARK).createMessage(READ);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(searchedbenchmarkid));

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderBenchmarkMessage(response.getBody());
    }

    @Override
    public Utilisateur updateUser(int userid, int groupid, Utilisateur template) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(USER).createMessage(UPDATE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(template.getIdPersonne()));

        request.addBody(XMLBuilder.BuildUserMessage(template));

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderUserMessage(response.getBody());
    }

    @Override
    public Groupe updateGroup(int userid, int groupid, Groupe template) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(GROUP).createMessage(UPDATE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(template.getIdGroupe()));

        request.addBody(XMLBuilder.BuildGroupMessage(template));

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderGroupMessage(response.getBody());
    }

    @Override
    public Probleme updateProblem(int userid, int groupid, Probleme template) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(PROBLEM).createMessage(UPDATE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(template.getIdProbleme()));

        request.addBody(XMLBuilder.BuildProblemaMessage(template));

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderProblemaMessage(response.getBody());
    }

    @Override
    public void deleteUser(int userid, int targetuserid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(USER).createMessage(DELETE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));

        request.addParameter(TARGET_ID, String.valueOf(targetuserid));

        JavaNetworkCommunication.sendMessage(request);
    }

    @Override
    public void deleteGroup(int userid, int targetgroupid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(GROUP).createMessage(DELETE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));

        request.addParameter(TARGET_ID, String.valueOf(targetgroupid));

        JavaNetworkCommunication.sendMessage(request);
    }

    @Override
    public void deleteProblem(int userid, int groupid, int targetproblemid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(PROBLEM).createMessage(DELETE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(targetproblemid));

        JavaNetworkCommunication.sendMessage(request);
    }

    @Override
    public void deleteResult(int userid, int groupid, int targetresultid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(RESULT).createMessage(DELETE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(targetresultid));

        JavaNetworkCommunication.sendMessage(request);
    }

    @Override
    public void deleteBenchmark(int userid, int groupid, int targetbenchmarkid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request;

        request = MessageAbstractFactory.getFactory(BENCHMARK).createMessage(DELETE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(TARGET_ID, String.valueOf(targetbenchmarkid));

        JavaNetworkCommunication.sendMessage(request);
    }

    @Override
    public Utilisateur authentify(String login, String password) throws DataNotFoundException {
        Message request = null;
        Message response = null;

        try {
            request = MessageAbstractFactory.getFactory(MANAGEMENT).createMessage(AUTHENTICATION);

            request.addParameter(LOGIN, login);
            request.addParameter(PASSWORD, password);
            
            response = JavaNetworkCommunication.sendMessage(request);

            System.out.println("Reponse :"+response.getBody());
            
            return XMLReader.ReaderUserMessage(response.getBody());
        } catch (UnauthorizedOperationException e) {
            //HELP You shouldn't be able to reach this statement... Don't be silly
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void joinGroup(String login, int groupid) throws DataNotFoundException {
        throw new UnsupportedOperationException("Feature not yet implemented");
    }

    @Override
    public Groupe[] getGroups() {
        Message request = null;
        Message response = null;

        try {
            request = MessageAbstractFactory.getFactory(MANAGEMENT).createMessage(FETCH_GROUPS);

            response = JavaNetworkCommunication.sendMessage(request);

            List<Groupe> groups = new ArrayList<Groupe>();
            Document groupsDOM = new SAXBuilder().build(new StringReader(response.getBody()));
            
            List<?> groupsPath = XPath.selectNodes(groupsDOM.getRootElement(), "//group");
            for (Object object : groupsPath) {
                Element groupElement = (Element) object;
                groups.add(XMLReader.buildGroup(groupElement));
                System.out.println("Name : "+groupElement.getName());
            }

            return groups.toArray(new Groupe[groups.size()]);
        } catch (DataNotFoundException e) {
            //HELP You shouldn't be able to reach this statement... Don't be silly
            e.printStackTrace();
        } catch (UnauthorizedOperationException e) {
            //HELP You shouldn't be able to reach this statement... Don't be silly
            e.printStackTrace();
        } catch (JDOMException e) {
            throw new NetworkException("Malformed Message : unable to parse content", e);
        } catch (IOException e) {
            throw new NetworkException("Malformed Message : unable to read content", e);
        }

        return null;
    }

    @Override
    public Utilisateur[] getUsers(int userid, int groupid) throws DataNotFoundException, UnauthorizedOperationException {

        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(MANAGEMENT).createMessage(FETCH_USERS);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        try {
            response = JavaNetworkCommunication.sendMessage(request);

            List<Utilisateur> users = new ArrayList<Utilisateur>();
            Document usersDOM = new SAXBuilder().build(new StringReader(response.getBody()));

            List<Element> path = XPath.selectNodes(usersDOM.getRootElement(), "//user");

            for (Element element : path) {
                users.add(XMLReader.buildUser(element));
            }

            return users.toArray(new Utilisateur[users.size()]);
        } catch (JDOMException e) {
            throw new NetworkException("Malformed Message : unable to parse content", e);
        } catch (IOException e) {
            throw new NetworkException("Malformed Message : unable to read content", e);
        }
    }

    @Override
    public Probleme[] getProblems(int userid, int groupid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(MANAGEMENT).createMessage(FETCH_PROBLEMS);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        try {
            response = JavaNetworkCommunication.sendMessage(request);

            List<Probleme> problems = new ArrayList<Probleme>();
            Document problemsDOM = new SAXBuilder().build(new StringReader(response.getBody()));

            List path = XPath.selectNodes(problemsDOM.getRootElement(), "//probleme");

            for (Object object : path) {
                Element element = (Element) object;
                problems.add(XMLReader.buildProblem(element));
            }

            return problems.toArray(new Probleme[problems.size()]);
        } catch (JDOMException e) {
            throw new NetworkException("Malformed Message : unable to parse content", e);
        } catch (IOException e) {
            throw new NetworkException("Malformed Message : unable to read content", e);
        }
    }

    @Override
    public Resultat[] getResults(int userid, int groupid, int targetproblemid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(MANAGEMENT).createMessage(FETCH_RESULTS);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(PROBLEM_ID, String.valueOf(targetproblemid));

        try {
            response = JavaNetworkCommunication.sendMessage(request);

            List<Resultat> results = new ArrayList<Resultat>();
            
            System.out.println("Before Dom");
            System.out.println("reponse.getBody vaut : "+response.getBody());
            
            Document resultsDOM = new SAXBuilder().build(new StringReader(response.getBody()));
            
            System.out.println("resultatDom : OK");

            List path = XPath.selectNodes(resultsDOM.getRootElement(), "//resultat-simple");

            for (Object object : path) {
                Element element = (Element)object;
                results.add(XMLReader.buildResult(element));
            }

            return results.toArray(new Resultat[results.size()]);
        } catch (JDOMException e) {
            throw new NetworkException("Malformed Message : unable to parse content", e);
        } catch (IOException e) {
            throw new NetworkException("Malformed Message : unable to read content", e);
        }
    }

    @Override
    public Benchmarks[] getBenchmarks(int userid, int groupid, int targetproblemid) throws DataNotFoundException, UnauthorizedOperationException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(MANAGEMENT).createMessage(FETCH_BENCHMARKS);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(PROBLEM_ID, String.valueOf(targetproblemid));

        try {
            response = JavaNetworkCommunication.sendMessage(request);

            List<Benchmarks> benchmarks = new ArrayList<Benchmarks>();
            Document benchmarksDOM = new SAXBuilder().build(new StringReader(response.getBody()));

            List path = XPath.selectNodes(benchmarksDOM.getRootElement(), "//resultat-benchmark");

            for (Object object: path) {
                Element element = (Element)object;
                benchmarks.add(XMLReader.buildBenchmarks(element));
            }

            return benchmarks.toArray(new Benchmarks[benchmarks.size()]);
        } catch (JDOMException e) {
            throw new NetworkException("Malformed Message : unable to parse content", e);
        } catch (IOException e) {
            throw new NetworkException("Malformed Message : unable to read content", e);
        }
    }

    @Override
    public Resultat resolve(int userid, int groupid, int problemid, ArrayList<Parameters> initialConditions, int methodid) throws DataNotFoundException, UnauthorizedOperationException, MathematicsException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(RESULT).createMessage(CREATE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(PROBLEM_ID, String.valueOf(problemid));
        request.addParameter(METHOD_ID, String.valueOf(methodid));

        Map<Parameters.InitialCondition,String> conditionsInitiales = listToMap(initialConditions);

        request.addBody( XMLBuilder.buildParametersMessage(conditionsInitiales) );

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderResultMessage(response.getBody());
    }

    @Override
    public Benchmarks resolve(int userid, int groupid, int problemid, ArrayList<Parameters> initialConditions, int... methodids) throws DataNotFoundException, UnauthorizedOperationException, MathematicsException {
        Message request = null;
        Message response = null;

        request = MessageAbstractFactory.getFactory(BENCHMARK).createMessage(CREATE);

        request.addParameter(CURRENT_USER_ID, String.valueOf(userid));
        request.addParameter(CURRENT_GROUP_ID, String.valueOf(groupid));

        request.addParameter(PROBLEM_ID, String.valueOf(problemid));
        request.addParameter(METHOD_IDS, Arrays.toString(methodids));
        
        //TODO Mettre en place les conditions initiales avec ArrayList<Parameters> qui se trouve dans les parametres de cette m�thode
        //Tu px recuperer le header du parametre et sa valeur avec les op�rations getType et getConten. Tu px aller voir la classe EcouteurExecBenchmark dans la clause
        //if prg ou pm !null pour voir ce qu'il se passe

        response = JavaNetworkCommunication.sendMessage(request);

        return XMLReader.ReaderBenchmarkMessage(response.getBody());
    }

    //FIXME A déplacer car sans rapport avec le réseau....
	@Override
	public void createPrintBenchmark(Benchmarks b) {
        XMLBuilder.BuildBenchmarkPrintMessage(b);
	}

    private Map<Parameters.InitialCondition, String> listToMap(ArrayList<Parameters> initialConditions) {
        Map<Parameters.InitialCondition,String> map = new HashMap<Parameters.InitialCondition, String>(initialConditions.size());
        for( Parameters p : initialConditions)
            if ( p.getType().equals("Initial-Vector") ) {
                map.put(Parameters.InitialCondition.INITIAL_VECTOR,p.getContent());
            }  else if ( p.getType().equals("Epsilon") ) {
                map.put(Parameters.InitialCondition.EPSILON,p.getContent());
            }  else if ( p.getType().equals("Iteration") ) {
                map.put(Parameters.InitialCondition.PATIENCE,p.getContent());
            }  else if (p.getType().equals("Step") ) {
                map.put(Parameters.InitialCondition.STEP,p.getContent());
            }  else if (p.getType().equals("Temperature") ) {
                map.put(Parameters.InitialCondition.TEMPERATURE,p.getContent());
            }  else if (p.getType().equals("Exploration-Step") ) {
                map.put(Parameters.InitialCondition.EXPLORATION_STEP,p.getContent());
            }  else if (p.getType().equals("Size-Of-Initial-Population") ) {
                map.put(Parameters.InitialCondition.POPULATION_SIZE,p.getContent());
            }  else if (p.getType().equals("Cross-Probability") ) {
                map.put(Parameters.InitialCondition.CROSS_PROBABILITY,p.getContent());
            }  else if (p.getType().equals("Mutation-Probability") ) {
                map.put(Parameters.InitialCondition.MUTATION_PROBABILITY,p.getContent());
            }  else if (p.getType().equals("Inferior-Limit") ) {
                map.put(Parameters.InitialCondition.BOUNDS_MIN,p.getContent());
            }  else if (p.getType().equals("Superior-Limit") ) {
                map.put(Parameters.InitialCondition.BOUNDS_MAX,p.getContent());
            }
        return map;
    }
}
