#include <iostream>
#include <stdexcept>

#include "xmlrpc-c/oldcppwrapper.hpp"

#include "DataType.hpp"
#include "XmlRpcFunction.hpp"
#include "XmlRpcClass.hpp"
#include "SystemProxy.hpp"

using namespace std;

#define NAME           "xml-rpc-api2cpp"
#define VERSION        "0.1"


//=========================================================================
//  function get_class_info
//=========================================================================
//  Connect to a remote server and extract the information we'll need to
//  build a proxy class.

XmlRpcClass get_class_info (string server_url,
			    string class_prefix,
			    string class_name)
{
    // Create a place to store our data.
    XmlRpcClass info(class_name);

    // Create a proxy class.
    SystemProxy system(server_url);

    // Fetch the full list of methods, and process the ones we want.
    XmlRpcValue methods = system.listMethods();
    size_t end = methods.arraySize();
    for (size_t i = 0; i < end; i++) {

	// Break the method name into two pieces.
	string method_prefix;
	string function_name;
	string method_name = methods.arrayGetItem(i).getString();
	size_t last_dot = method_name.rfind('.');
	if (last_dot == string::npos) {
	    function_name = method_name;
	} else {
	    method_prefix = string(method_name, 0, last_dot);
	    function_name = string(method_name, last_dot + 1);
	}

	// Decide whether we care about this function.
	if (method_prefix == class_prefix) {

	    // Fetch some information about the function.
	    string help = system.methodHelp(method_name);
	    XmlRpcValue signature = system.methodSignature(method_name);

	    // Add this function to our class information.
	    XmlRpcFunction func(function_name, method_name, help, signature);
	    info.addFunction(func);
	}
    }

    return info;
}


//=========================================================================
//  function print_header
//=========================================================================
//  Print a complete header for the specified class.

void print_header (ostream& out, XmlRpcClass& class_info) {
    string class_name = class_info.className();
    out << "// " << class_name << ".h - xmlrpc-c C++ proxy class" << endl;
    out << "// Auto-generated by xml-rpc-api2cpp." << endl;
    out << endl;

    string header_symbol = "_" + class_name + "_H_";
    out << "#ifndef " << header_symbol << endl;
    out << "#define " << header_symbol << " 1" << endl;
    out << endl;
    out << "#include <XmlRpcCpp.h>" << endl;
    out << endl;

    class_info.printDeclaration(cout);

    out << endl;
    out << "#endif /* " << header_symbol << " */" << endl;
}


//=========================================================================
//  function print_cc_file
//=========================================================================
//  Print a complete header for the specified class.

void print_cc_file (ostream& out, XmlRpcClass& class_info) {
    string class_name = class_info.className();
    out << "// " << class_name << ".cc - xmlrpc-c C++ proxy class" << endl;
    out << "// Auto-generated by xml-rpc-api2cpp." << endl;
    out << endl;

    out << "#include <XmlRpcCpp.h>" << endl;
    out << "#include \"" << class_name << ".h\"" << endl;

    class_info.printDefinition(cout);
}


//=========================================================================
//  function main
//=========================================================================
//  For now, just a test harness.

int main (int argc, char **argv) {

    /* Parse our command-line arguments. */
    if (argc != 4) {
	cerr << argv[0] << ": Usage:" << endl
	     << "  xml-rpc-api2cpp <server_url> <method_prefix> <local_class>"
	     << endl << endl
	     << "Sample arguments:" << endl
	     << "  server_url = http://localhost/RPC2" << endl
	     << "  method_prefix = system" << endl
	     << "  local_class = SystemProxy" << endl;
	exit(1);
    }
    string server_url = argv[1];
    string method_prefix = argv[2];
    string local_class = argv[3];

    int status = 0;
    XmlRpcClient::Initialize(NAME, VERSION);

    try {
	XmlRpcClass system = get_class_info(server_url,
					    method_prefix,
					    local_class);
	print_header(cout, system);
	cout << endl;
	print_cc_file(cout, system);
    } catch (XmlRpcFault& fault) {
	cerr << argv[0] << ": XML-RPC fault #" << fault.getFaultCode()
	     << ": " << fault.getFaultString() << endl;
	status = 1;
    } catch (logic_error& err) {
	cerr << argv[0] << ": " << err.what() << endl;
	status = 1;
    } catch (...) {
	cerr << argv[0] << ": Unknown exception" << endl;
	status = 1;
    }

    XmlRpcClient::Terminate();

    return status;
}
