/*
 * main.cxx
 *
 * PWLib application source file for XMLRPCApp
 *
 * Main program entry point.
 *
 * Copyright 2002 Equivalence
 *
 * $Log: main.cxx,v $
 * Revision 1.7  2003/09/26 13:41:32  rjongbloed
 * Added special test to give more indicative error if try to compile without Expat support.
 *
 * Revision 1.6  2003/04/15 03:00:41  robertj
 * Added array support to XML/RPC
 * Fixed XML/RPC parsing when lots of white space in raw XML, caused by
 *   big fix to base XML parser not returning internal data elements.
 *
 * Revision 1.5  2002/12/04 02:09:17  robertj
 * Changed macro name prefix to PXMLRPC
 *
 * Revision 1.4  2002/12/04 00:16:18  robertj
 * Large enhancement to create automatically encoding and decoding structures
 *   using macros to build a class.
 *
 * Revision 1.3  2002/10/04 05:16:44  craigs
 * Changed for new XMLRPC code
 *
 * Revision 1.2  2002/03/27 01:54:40  craigs
 * Added ability to send random struct as request
 * Added ability to preview request without sending
 *
 * Revision 1.1  2002/03/26 07:05:28  craigs
 * Initial version
 *
 */

/*

  Example command lines

    http://time.xmlrpc.com/RPC2 currentTime.getCurrentTime 

    http://www.mirrorproject.com/xmlrpc mirror.Random

    http://xmlrpc.usefulinc.com/demo/server.php system.listMethods
    http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoString "A test!"
    -i http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoInteger 12
    -f http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoFloat 3.121
    -a http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoStringArray One Two Three Four
    -a -i http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoIntegerArray 11 222 3333 44444
    -a -f http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoIntegerArray 1.1 22.23 333.333 4444.4444
    -s http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoStruct first 1st second 2nd third 3rd
    -a -s http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoStructArray first 1st second 2nd third 3rd "," fourth 4th fifth 5th "," sixth 6th "," seventh 7th eigth 8th ninth 9th tenth 10th
    --echo-struct http://xmlrpc.usefulinc.com/demo/server.php interopEchoTests.echoStruct
    -s http://10.0.2.13:6666/RPC2 Function1 key value


 */

#include <ptlib.h>
#include "main.h"

#include <ptclib/pxmlrpc.h>

#if !P_EXPAT
#error Must have Expat XML support for this application
#endif



PXMLRPC_STRUCT_BEGIN(NestedStruct)
    PXMLRPC_STRING  (NestedStruct, PString, another_string);
    PXMLRPC_INTEGER (NestedStruct, int, another_integer);
PXMLRPC_STRUCT_END()

PXMLRPC_STRUCT_BEGIN     (TestStruct)
    PXMLRPC_STRING_INIT  (TestStruct, PString, a_string, "A string!");
    PXMLRPC_INTEGER_INIT (TestStruct, int, an_integer, 12);
    PXMLRPC_BOOLEAN_INIT (TestStruct, BOOL, a_boolean, TRUE);
    PXMLRPC_DOUBLE_INIT  (TestStruct, double, a_float, 3.14159);
    PXMLRPC_DATETIME     (TestStruct, PTime, a_date);
    PXMLRPC_BINARY       (TestStruct, PBYTEArray, a_binary);
    PXMLRPC_ARRAY_STRING (TestStruct, PStringArray, PCaselessString, a_string_array);
    PXMLRPC_ARRAY_INTEGER(TestStruct, int, an_integer_array);
    PXMLRPC_ARRAY_DOUBLE (TestStruct, float, a_float_array);
    PXMLRPC_STRUCT       (TestStruct, NestedStruct, nested_struct);
    PXMLRPC_ARRAY_STRUCT (TestStruct, NestedStruct, array_struct);
PXMLRPC_STRUCT_END()
 

PCREATE_PROCESS(XMLRPCApp);


/////////////////////////////////////////////////////////////////////////////

XMLRPCApp::XMLRPCApp()
  : PProcess("Equivalence", "XMLRPCApp", 1, 0, AlphaCode, 1)
{
}

void XMLRPCApp::Main()
{
  PINDEX i;
  PArgList & args = GetArguments();

  args.Parse("a-array."
             "d-debug."
             "f-float."
             "i-integer."
             "o-output:"
             "s-struct."
             "t-trace."
             "-echo-struct."
             );

  PTrace::Initialise(args.GetOptionCount('t'),
                     args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL);

  if (args.GetCount() < 2) {
    PError << "usage: xmlrpc url method [parms...]" << endl;
    return;
  }

  PString url    = args[0];
  PString method = args[1];

  PXMLRPC rpc(url);

  PXMLRPCBlock request(method);
  PXMLRPCBlock response;

  if (args.HasOption("echo-struct")) {
    TestStruct ts;
    ts.a_date -= PTimeInterval(0, 0, 0, 0, 5);

    ts.a_binary.SetSize(10);
    for (i = 0; i < 10; i++)
      ts.a_binary[i] = (BYTE)(i+1);

    ts.a_string_array.SetSize(3);
    ts.a_string_array[0] = "first";
    ts.a_string_array[1] = "second";
    ts.a_string_array[2] = "third";

    ts.an_integer_array.SetSize(7);
    for (i = 0; i < ts.an_integer_array.GetSize(); i++)
      ts.an_integer_array[i] = i+1;

    ts.a_float_array.SetSize(5);
    for (i = 0; i < ts.a_float_array.GetSize(); i++)
      ts.a_float_array[i] = (float)(1.0/(i+2));

    ts.nested_struct.another_string = "Another string!";
    ts.nested_struct.another_integer = 345;

    ts.array_struct.SetSize(2);
    ts.array_struct.SetAt(0, new NestedStruct);
    ts.array_struct[0].another_string = "Structure one";
    ts.array_struct[0].another_integer = 11111;
    ts.array_struct.SetAt(1, new NestedStruct);
    ts.array_struct[1].another_string = "Structure two";
    ts.array_struct[1].another_integer = 22222;
    request.AddParam(ts);
  }
  else {
    if (args.HasOption('a')) {
      if (args.HasOption('s')) {
        PArray<PStringToString> array;
        PStringToString dict;
        PString key;
        for (i = 2; i < args.GetCount(); i++) {
          if (args[i] == ",") {
            array.SetAt(array.GetSize(), new PStringToString(dict));
            dict = PStringToString();
            key = PString::Empty();
          }
          else if (key.IsEmpty())
            key = args[i];
          else {
            dict.SetAt(key, args[i]);
            key = PString::Empty();
          }
        }

        if (!dict.IsEmpty())
          array.SetAt(array.GetSize(), new PStringToString(dict));

        request.AddArray(array);
      }
      else if (args.HasOption('i'))
        request.AddArray(args.GetParameters(2), "int");
      else if (args.HasOption('f'))
        request.AddArray(args.GetParameters(2), "double");
      else
        request.AddArray(args.GetParameters(2));
    }
    else if (args.HasOption('s')) {
      PStringToString dict;
      for (i = 2; (i+1) < args.GetCount(); i += 2) {
        PString key   = args[i];
        PString value = args[i+1];
        dict.SetAt(key, value);
      }

      request.AddStruct(dict);
    }
    else {
      for (i = 2; i < args.GetCount(); i++) {
        if (args.HasOption('i'))
          request.AddParam(args[i].AsInteger());
        else if (args.HasOption('f'))
          request.AddParam(args[i].AsReal());
        else
          request.AddParam(args[i]);
      }
    }
  }

  if (args.HasOption('d'))
    cout << "Request = " << request << endl;

  if (!rpc.MakeRequest(request, response)) {
    PError << "Error in request (" 
           << rpc.GetFaultCode() 
           << ") : "
           << rpc.GetFaultText()
           << endl;
    return;
  }

  // scan through the response and print it out
  cout << "Response" << endl;
  for (i = 0; i < response.GetParamCount(); i++) {
    cout << "  " << i << ": ";
    PString type;
    PString val;
    if (response.GetParam(i, type, val)) {
      cout << type << " = ";
      if (type == "struct") {
        PStringToString dict;
        response.GetParam(i, dict);
        cout << '\n' << dict;
      }
      else if (type == "array") {
        PStringArray array;
        response.GetParam(i, array);
        cout << '\n' << setfill('\n') << array << setfill(' ');
      }
      else
        cout << val;
    }
    else
      cout << "error: " << response.GetFaultText();
    cout << endl;
  }

  if (args.HasOption("echo-struct")) {
    TestStruct ts;
    ts.a_date = PTime(0);
    if (response.GetParam(0, ts))
      cout << "Parsed response:\n" << ts;
    else
      cout << "Failed to parse resonse: " << response.GetFaultText();
    cout << endl;
  }
}

// End of File ///////////////////////////////////////////////////////////////


syntax highlighted by Code2HTML, v. 0.9.1