#include <iostream>
#include <assert.h>
#include <time.h>

#include "../include/TestValues.h"
#include "../include/Mole.h"

#include "../ServiceManager.hpp"
#include "../Request.hpp"
#include "../include/XmlRpc.h"
#include "../Server.hpp"
#include "../XmlRpcppClient.hpp"

#include "../responses/HelloResponse.hpp"
#include "../responses/ConnectionResponse.hpp"
#include "../responses/ListChannelsResponse.hpp"
#include "../responses/JoinChannelResponse.hpp"
#include "../responses/ListUsersResponse.hpp"

#include "../services/HelloService.hpp"
#include "../services/ConnectionService.hpp"
#include "../services/ListChannelsService.hpp"
#include "../services/ListUsersService.hpp"
#include "../services/JoinChannelService.hpp"

#include "../requests/HelloRequest.hpp"
#include "../requests/ConnectionRequest.hpp"
#include "../requests/ListChannelsRequest.hpp"
#include "../requests/ListUsersRequest.hpp"
#include "../requests/JoinChannelRequest.hpp"

#include "../requests/xmlrpcpp/HelloRequest.hpp"
#include "../requests/xmlrpcpp/ConnectionRequest.hpp"
#include "../requests/xmlrpcpp/ListChannelsRequest.hpp"
#include "../requests/xmlrpcpp/ListUsersRequest.hpp"
#include "../requests/xmlrpcpp/JoinChannelRequest.hpp"

#define LOGIN_SIZE 8

using namespace XmlRpc;
using namespace mole;

/** Genere un login aleatoire qui peut etre utilise pour la connection. */
std::string randomLogin(){
    int i;
    std::string l;
    srand(time(NULL));
    for (i = 0; i < LOGIN_SIZE; i++)
	l += (rand() % 26) + 97;
    return l;
}


/* Test du service HELLO */
bool
testHelloService(ctx::Client * rpcClient, std::string in){
    serv::Service * service = rpcClient->getService(std::string(NAME_SERV_HELLO));
    if (service){
	ctx::HelloRequest * request = static_cast <ctx::HelloRequest *> (service->getRequest());  
	request->init(in);
	if (rpcClient->execute(service)){
	    serv::HelloResponse * resp = static_cast <serv::HelloResponse *> (service->getResponse());
	    if (resp){
		std::cout << "Invocation de Hello : " << resp->getText() << "\n";
		delete service;
		return true;
	    }
	    else
		std::cout << "Invocation de Hello failure \n";
	}
    } else
	std::cout << "No service named " << NAME_SERV_HELLO << "\n";
    delete service;
    return false;
}

/* Test du service de connection */
bool
testConnectionService(ctx::Client * client, std::string login, std::string ip,	int port){
    serv::Service * service = client->getService(std::string(NAME_SERV_CONNECT));
    if (service){
	ctx::ConnectionRequest * request = static_cast <ctx::ConnectionRequest *> (service->getRequest());
	request->init(login, ip, port);
	if (client->execute(service)){
	    serv::ConnectionResponse * resp = static_cast <serv::ConnectionResponse *> (service->getResponse());
	    if (resp){
		if (resp->errorCode() != 0){
		    std::cout << "Error #" << resp->errorCode() << " : " << resp->errorString() << "\n";
		    /* We spam the service until we get a correct login & connect */
		    login = randomLogin();
		    std::cout << "Trying with " << login << " as login\n";
		    while (!testConnectionService(client, login, ip, port));
		} else {
		    std::cout << "Invocation de Connection : " << resp->getLogin() << "-" << resp->getId() << "\n";
		}
	    } else
		std::cout << "Invocation de connection : failure\n";

	    delete service;
	    // We always return true if a response is given from service
	    return true;
	}
    } else {
	std::cout << "No service named " << NAME_SERV_CONNECT << "\n";
    }
    delete service;
    return false;
}

/* Test du service de listing de channels */
bool
testListChannelsService(ctx::Client * client){
    serv::Service * service = client->getService(std::string(NAME_SERV_LISTCHANNELS));
    if (service){
	ctx::ListChannelsRequest * request = static_cast <ctx::ListChannelsRequest *> (service->getRequest());
	if (client->execute(service)){
	    serv::ListChannelsResponse * resp = static_cast <serv::ListChannelsResponse *> (service->getResponse());
	    if (resp)
		if (resp->errorCode() != 0){
		    std::cout << "Error #" << resp->errorCode() << " : " << resp->errorString() << "\n";
		} else {
		    std::cout << "Invocation de ListChannels : " << "\n";
		    for (int i = 0; i < resp->channelsCount(); i++)
			std::cout << "#" << i << " " << resp->getChannel(i) << "\n";
		    delete service;
		    return true;
		}
		else
		    std::cout << "Invocation de list channels : failure\n";
	} else
	    std::cout << "Service failed!\n";
    } else {
	std::cout << "No service named " << NAME_SERV_LISTCHANNELS << """\n";
    }
    delete service;
    return false;
}

/* Test la connection a un channel identifie par son nom */
bool
testJoinChannelService(ctx::Client * client, std::string chan){
    serv::Service * service = client->getService(NAME_SERV_JOINCHANNEL); 
    if (service){
	ctx::JoinChannelRequest * request = static_cast <ctx::JoinChannelRequest *> (service->getRequest());
	request->init(chan);
	if (client->execute(service)){
	    serv::JoinChannelResponse * resp = static_cast <serv::JoinChannelResponse *> (service->getResponse());
	    if (resp)
		if (resp->errorCode() != 0){
		    std::cout << "Error #" << resp->errorCode() << " : " << resp->errorString() << "\n";
		} else {
		    std::cout << "Invocation de JoinChannel : " << resp->getResult() << "\n";
		    delete service;
		    return true;
		}
		else
		    std::cout << "Invocation de join channel : failure\n";
	} else
	    std::cout << "Service failed\n";
    } else {
	std::cout << "No service named " << NAME_SERV_JOINCHANNEL << """\n";
    }
    delete service;
    return false;
}

/* Test du listing des utilisateurs connectes a un salon */ 
bool
testListUsersService(ctx::Client * client, std::string chan){
    serv::Service * service = client->getService(NAME_SERV_LISTUSERS); 
    if (service){
	ctx::ListUsersRequest * request = static_cast <ctx::ListUsersRequest *> (service->getRequest());
	request->init(chan);
	if (client->execute(service)){
	    serv::ListUsersResponse * resp = static_cast <serv::ListUsersResponse *> (service->getResponse());
	    if (resp)
		if (resp->errorCode() != 0){
		    std::cout << "Error #" << resp->errorCode() << " : " << resp->errorString() << "\n";
		} else {
		    std::cout << "Invocation de ListUsers : " << "\n";
		    for (int i = 0; i < resp->usersCount(); i++)
			std::cout << "#" << i << " " << resp->getUser(i) << "\n";
		    delete service;
		    return true;
		}
		else
		    std::cout << "Invocation de list users : failure\n";
	} else
	    std::cout << "Service failed\n";
    } else {
	std::cout << "No service named " << NAME_SERV_LISTUSERS << "\n";
    }
    delete service;
    return false;
}

/**
 * Instances a xmlrpc client and connects to a server,
 * and then try to invoque some services onto it.
 */
int
main(int argc, char ** argv){
    if (argc != 4){
	std::cerr << "Usage : TestClient ip port path\nExiting...\n";
	return EXIT_FAILURE;  
    }

    /* Creates a RPC Client */
    ctx::xmlrpcpp::XmlRpcppClient * rpcClient = new ctx::xmlrpcpp::XmlRpcppClient(argv[1], atoi(argv[2]), argv[3]);
    rpcClient->init();
    assert(rpcClient->connect());
    std::cout << "Connection to the server done.\n";

    /* First of all, have to registers all services purposed
     * by the server binded to. ServiceManager does that for us. */
    serv::ServiceManager * manager = new serv::ServiceManager(rpcClient);
    manager->loadServices();

    assert(testHelloService(rpcClient, TEST_HELLO)); 
    assert(testConnectionService(rpcClient, TEST_LOGIN, TEST_IP, TEST_PORT));
    assert(testListChannelsService(rpcClient));
    assert(testJoinChannelService(rpcClient, TEST_CHAN_TO_JOIN));
    assert(testListUsersService(rpcClient, TEST_CHAN_TO_JOIN));

    delete rpcClient;
    return EXIT_SUCCESS;
}
