/**
@mainpage

	address.cpp

	Address book example XML data binding application.

	Illustrates the gSOAP XML data binding with wsdl2h and soappcp2.

	Copyright (C) 2000-2009 Robert A. van Engelen. All Rights Reserved.

	Project Source Files
	- address.xsd		Address book
	- address.cpp		Address book app (reads/writes address.xml file)
	- addresstypemap.dat	Schema namespace prefix name preference

	Generated Files
	- address.h		gSOAP data binding definitions from address.xsd
	- addressStub.h		C++ data binding definitions
	- addressH.h		Serializers
	- addressC.cpp		Serializers
	- a.nsmap		XML namespace mapping file (binds 'a' URI)
	- address.xml		Address book data generated by address

	Compile:
	- wsdl2h -g -t addresstypemap.dat address.xsd
	- soapcpp2 -2 -CS -I../../import -p address address.h
	- g++ -I../.. address.cpp addressC.cpp -o address ../../stdsoap2.cpp

	Run:
	- ./address

	Additional notes:
	- To instantiate class X in the engine's data space, we use the
	  auto-generated soap_new_X(soap, -1). This allows easy deallocation
	  with soap_destroy(soap). Use soap_new_X(soap, N) to allocate an array
	  of N objects
	- X::soap_default(soap) sets X members to default values. For C types
	  use soap_default_X(soap, X*) to initialize values of type X
	- To convert enum constants to string, we use the auto-generated
	  soap_X2s(soap, enum X) function
	- To convert a string to an enum constant, we use the auto-generated
	  soap_s2X(soap, const char*, enum X*) function
	- The struct soap context is initialized with the following flags:
	  SOAP_XML_STRICT:	strictly validates XML (reader);
	  SOAP_XML_INDENT:	produces indented XML (writer);
	  SOAP_XML_TREE:	output tree XML for co-referenced objects in
				object graph. Otherwise, co-referenced objects
				are encoded as per SOAP multi-ref encoding
				(writer);
	  SOAP_XML_CANONICAL:	improves XML output by removing unnecessary
	  			xmlns bindings (writer).
	- If multi-ref objects are serialized with SOAP encoding, the
	  co-references are preserved in the XML-serialized output using 'id'
	  and 'ref' attributes for cross referencing. To ensure an accurate
	  mapping of the XML object graph after serilaization. We advice to use
	  the SOAP1.2 standard. Therefore, soapcpp2 option -2 is used. The
	  SOAP_XML_STRICT and SOAP_XML_CANONICAL flags cannot be used with SOAP
	  encoding formats.
*/
/*
--------------------------------------------------------------------------------
gSOAP XML Web services tools
Copyright (C) 2001-2009, Robert van Engelen, Genivia, Inc. All Rights Reserved.
This software is released under one of the following two licenses:
GPL or Genivia's license for commercial use.
--------------------------------------------------------------------------------
GPL license.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA

Author contact information:
engelen@genivia.com / engelen@acm.org
--------------------------------------------------------------------------------
A commercial use license is available from Genivia, Inc., contact@genivia.com
--------------------------------------------------------------------------------
*/

#include <iostream>
#include <fstream>

#include "addressH.h"	// generated, also includes stdsoap2.h
#include "a.nsmap"	// generated

/**
@fn char *user_input(const char *prompt)
@brief A quick-and-dirty user input function: reads string from stdin (up to 80
chars), trims leading and trailing blanks, and returns it
@param prompt displays prompt to user
@return string input
*/
char *user_input(const char *prompt);

/**
@brief Address book application: reads address.xml, displays its content,
prompts the user for new contact, adds it, and saves the updated address.xml.
*/
int main()
{
  // New soap struct engine context
  // Use strict validation and indented canonicalized output
  struct soap *soap = soap_new1(SOAP_XML_STRICT | SOAP_XML_INDENT | SOAP_XML_CANONICAL);

  _a__address_book *ab = soap_new__a__address_book(soap, -1);

  std::fstream fs;

  // Read the address book from address.xml (defined by address.xsd)
  fs.open("address.xml", std::ios::in);
  if (fs)
  {
    soap->is = &fs;
    if (soap_read__a__address_book(soap, ab) != SOAP_OK)
    { 
      std::cerr << "Error reading address.xml file" << std::endl;
      soap_stream_fault(soap, std::cerr);
      exit(1);
    }
    fs.close();
  }

  // Display the address book content
  std::cout << std::endl << "ADDRESS BOOK - An Example XML Data Binding Application" << std::endl << std::endl;
  for (std::vector<a__address*>::const_iterator i = ab->address.begin(); i != ab->address.end(); ++i)
  {
    if (*i)
    {
      std::cout << "Address entry " << (*i)->ID << std::endl;
      std::cout << "Name:    " << (*i)->name << std::endl;
      std::cout << "Street:  " << (*i)->street << std::endl;
      std::cout << "City:    " << (*i)->city << std::endl;
      std::cout << "Zip:     " << (*i)->zip << std::endl;
      // Advanced level: we use the soapcpp2-generated soap_a__ISO_country2s()
      // function to convert enum a__ISO_country values to strings. The strings
      // are allocated in the gSOAP engine and deleted with soap_end()
      std::cout << "Country: " << soap_a__ISO_country2s(soap, (*i)->country) <<
      std::endl;
      if ((*i)->phone)
        std::cout << "Phone:   " << *(*i)->phone << std::endl;
      if ((*i)->mobile)
        std::cout << "Mobile:  " << *(*i)->mobile << std::endl;
      // Advanced level: use the soap_dateTime2s() from the stdsoap2.cpp engine
      if ((*i)->dob)
        std::cout << "DOB:     " << soap_dateTime2s(soap, *(*i)->dob) << std::endl;
      std::cout << "---------" << std::endl;
    }
  }

  // Allocate a new address in the gSOAP engine's data space
  a__address *a = soap_new_a__address(soap, -1);
  // Set object's default values (soap_default is generated)
  a->soap_default(soap);

  a->ID = ab->address.size() + 1;

  std::cout << "Enter a new contact:" << std::endl;
  a->name = user_input("Name");
  a->street = user_input("Street");
  a->city = user_input("City");
  a->zip = user_input("Zip");
  char *s = user_input("Country");
  // Advanced level: use the generated s2a__ISO_country() to convert string to
  // enum constant
  if (soap_s2a__ISO_country(soap, s, &a->country) != SOAP_OK)
    std::cerr << "Not a valid country code" << std::endl;
  if (*(s = user_input("Phone")))
  {
    // Allocate string in engine's data space:
    a->phone = soap_new_std__string(soap, -1);
    *a->phone = s;
  } 
  if (*(s = user_input("Mobile")))
  {
    // Allocate string in engine's data space:
    a->mobile = soap_new_std__string(soap, -1);
    *a->mobile = s;
  } 

  // Add contact to address book
  ab->address.push_back(a);

  std::cout << std::endl << "Contact information added." << std::endl;

  // Save updated address book to address.xml
  fs.open("address.xml", std::ios::out);
  if (!fs)
  {
    std::cerr << "Cannot create address.xml file" << std::endl;
    exit(1);
  }
  soap->os = &fs;
  if (soap_write__a__address_book(soap, ab) != SOAP_OK)
  {
    std::cerr << "Error writing address.xml file" << std::endl;
    soap_stream_fault(soap, std::cerr);
    exit(1);
  }
  fs.close();

  // Delete instances
  soap_destroy(soap);
  // Delete data
  soap_end(soap);
  // Free soap struct engine context
  soap_free(soap);

  return 0;
}

char *user_input(const char *prompt)
{
  static char buf[80];
  char *s;

  printf("%-9s> ", prompt);
  fgets(buf, 80, stdin);

  // Strip trailing space
  for (s = buf + strlen(buf) - 1; s > buf; s--)
  {
    if (*s > ' ')
      break;
  }
  s[1] = '\0';

  // Strip leading space
  for (s = buf; *s; s++)
  {
    if (*s > ' ')
      break;
  }

  return s;
}
