//---------------------------------------------------------------------------
//
// MODULE NAME:     main.cpp
//
// COPYRIGHT:       Copyright (c) 2013, GladeSoft, Inc.
//                  All Rights Reserved.
//
//                  NOTICE: THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF
//                  GLADESOFT, INC., AND SHALL NOT BE REPRODUCED, COPIED
//                  IN WHOLE OR IN PART ADAPTED, MODIFIED, OR
//                  DISSEMINATED WITHOUT THE EXPRESS WRITTEN CONSENT
//                  OF GLADESOFT, INC. ANY REDISTRIBUTION OR
//                  PUBLICATION TO UNAUTHORIZED PERSONS IS STRICTLY
//                  PROHIBITED BY U.S. AND INTERNATIONAL COPYRIGHT LAW.
//
// DESCRIPTION:     XML API Example
//
// AUTHOR:          M. Grosberg
//
// CREATION DATE:   Jul 22, 2013
//
//---------------------------------------------------------------------------
//
// HISTORY OF CHANGE  
//
// -----+--------+------------------------------------------+------+---------
// VERS | DATE   | DESCRIPTION OF CHANGE                    | INIT | PR#
// =====+========+==========================================+======+=========
//  001 |07/22/13| File creation.                           | MYG  |
// -----+--------+------------------------------------------+------+---------
// -----+--------+------------------------------------------+------+---------
//---------------------------------------------------------------------------

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "seminole.h"
#include "sem_xml.h"

class ChattyXmlDomBuilder : public HttpdXmlDomBuilder
{
protected:
  virtual int Error(int error_type, ...);

public:
  ChattyXmlDomBuilder(HttpdXmlHost &host,
                      HttpdUint8 flags = 0, size_t initial_buffer = 0,
                      size_t max_buffer = ~(size_t )0)
    : HttpdXmlDomBuilder(host, flags, initial_buffer, max_buffer) {}
};

//---------------------------------------------------------------------------
//
// NAME:           ChattyXmlDomBuilder::Error
//
// DESCRIPTION:    Overridden error processing method for logging XML
//                 parse errors.
//
// RETURNS:        Status code.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
int ChattyXmlDomBuilder::Error(int error_type, ...)
{
  va_list va;

  va_start(va, error_type);
  switch (error_type)
  {
    case XML_ERR_EXPECTED:
    {
      const char *p_got  = va_arg(va, const char *);
      const char *p_want = va_arg(va, const char *);
      printf("At line #%d: Expected %s but got %s\n",
             (int )mLineNumber, p_want, p_got);
      break;
    }

    case XML_ERR_UNEXPECTED:
      printf("At line #%d: Unexpected %s\n",
             (int )mLineNumber,
             va_arg(va, const char *));
      break;

    case XML_ERR_UNKNOWN_ENTITY:
      printf("At line #%d: Unknown entity reference '%s'\n",
             (int )mLineNumber,
             va_arg(va, const char *));
      break;

    case XML_ERR_EARLY_EOF:
      printf("Unexpected end-of-stream at line #%d\n",
             (int )mLineNumber);
      break;

    case XML_ERR_UNKNOWN_NS_SELECTOR_ON_ATTR:
    {
      const char *p_selector  = va_arg(va, const char *);
      const char *p_attribute = va_arg(va, const char *);

      printf("Invalid namespace selector %s on attribute %s at line #%d\n",
             p_selector, p_attribute, (int )mLineNumber);
      break;
    }

    case XML_ERR_EMPTY_NS_SELECTOR:
      printf("Missing namespace name on %s at line #%d\n",
             va_arg(va, const char *), (int )mLineNumber);
      break;

    case XML_ERR_UNKNOWN_NS_SELECTOR_ON_NODE:
    {
      const char *p_selector = va_arg(va, const char *);
      const char *p_tag      = va_arg(va, const char *);

      printf("Unknown namespace selector %s used on %s at line #%d\n",
             p_selector, p_tag, (int )mLineNumber);
      break;
    }

    case XML_ERR_RESERVED_SELECTOR:
    {
      const char *p_selector = va_arg(va, const char *);
      const char *p_value    = va_arg(va, const char *);

      printf("Invalid value %s for reserved selector name %s at line #%d\n",
             p_value, p_selector, (int )mLineNumber);
      break;
    }

    case XML_ERR_UNCLOSED_TAG:
      printf("Unclosed tag %s at line #%d\n",
             va_arg(va, const char *), (int )mLineNumber);
      break;

    default:
      printf("Unknown error parsing XML at line #%d\n",
             (int )mLineNumber);
      break;
  }

  va_end(va);
  return (HttpdOpSys::ERR_BADPARAM);
}

//**********************************************************************

class FileWriter : public HttpdWritable
{
  FILE *mpFile;

public:
  int Write(size_t n, const void *d);
  FileWriter(FILE *p) : mpFile(p) {}
};

//---------------------------------------------------------------------------
//
// NAME:           FileWriter::Write
//
// DESCRIPTION:    Implement the HttpdWritable interface on an STDIO stream.
//
// RETURNS:        Status code.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
int FileWriter::Write(size_t bytes, const void *p_data)
{
  if (fwrite(p_data, 1, bytes, mpFile) != bytes)
    return (HttpdOpSys::ERR_IO);
  else
    return (0);
}

//**********************************************************************

static const char gXmlDocument[] =
  "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>"
  "<world>"
  " <object>"
  "  <mass units=\"grams\">30</mass>"
  "  <density units=\"kg/m3\">980</density>"
  "  <pH>7</pH>"
  " </object>"
  " <object>"
  "  <mass units=\"lbs\">0.8</mass>"
  "  <color>blue</color>"
  " </object>"
  " <container shape=\"square\">"
  "  <object>"
  "   <color>purple</color>"
  "   <mass units=\"kg\">0.5</mass>"
  "   <melting-point units=\"degC\">670</melting-point>"
  "  </object>"
  "  <object>"
  "   <color>green</color>"
  "   <mass units=\"lbs\">2</mass>"
  "   <refractive-index>1.6</refractive-index>"
  "  </object>"
  " </container>"
  "</world>";

//---------------------------------------------------------------------------
//
// NAME:           RunQueries
//
// DESCRIPTION:    Query the document for various things.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
static void RunQueries(const HttpdXmlDomBuilder &builder)
{
  // What is the pH of the first object in the world?
  printf("pH of the first object is %s\n", builder.Lookup("world/object/pH"));

  // What is the mass of the second object in the world?
  printf("The second object weighs %s %s\n",
         builder.Lookup("world/object>1/mass"),
         builder.Lookup("world/object>1/mass&units"));

  // Find the container in the world. 
  HttpdXmlDomNode *p_container = builder.LookupNode("world/container");

  // Iterate the children of the container.
  HttpdListIterator walk(p_container->Children().Head());
  while (walk.Continue())
  {
    HttpdXmlDomNode *p_contained = (HttpdXmlDomNode *)(void *)walk;

    printf("Inside the container we have a %s %s\n", 
           p_contained->Lookup("color"), p_contained->Tag());

    const char *p_mass       = p_contained->Lookup("mass");
    const char *p_mass_units = p_contained->Lookup("mass&units");

    if ((p_mass != NULL) && (p_mass_units != NULL))
      printf("... and it weighs %s %s\n", p_mass, p_mass_units);

    walk.Next();
  }
}

//---------------------------------------------------------------------------
//
// NAME:           main
//
// DESCRIPTION:    Entry point for the demo.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
int main(int argc, const char **argv)
{
  // Initialize Seminole.
  if (Httpd::Init() != 0)
  {
    printf("Can't initialize Seminole\n");
    return (EXIT_FAILURE);
  }

  // Create an XML DOM parser.
  HttpdXmlHost         host;
  ChattyXmlDomBuilder  document(host);
  int                  rc;

  rc = document.Create();
  if (rc != 0)
  {
    printf("Can't create document parser\n");
    return (EXIT_FAILURE);
  }

  // Write in the XML.
  rc = document.WriteString(gXmlDocument);

  // Conclude parsing.
  if (rc == 0)
    rc = document.Finish();

  if (rc != 0)
  {
    printf("Can't parse XML document\n");
    return (EXIT_FAILURE);
  }

  // Select data from the document.
  RunQueries(document);

  // Alter the in-memory document.
  rc = document.Set("world/container/object>1/color", "orange");
  if (rc != 0)
  {
    printf("Can't adjust XML document\n");
    return (EXIT_FAILURE);
  }

  // Serialize the new XML.
  FileWriter         fw(stdout);
  HttpdXmlDomWriter  dw(&fw, 2, 0, 0);

  printf("Updated document:\n");
  rc = dw.WriteDom(&document);
  if (rc != 0)
  {
    printf("Can't serialize document\n");
    return (EXIT_FAILURE);
  }

  return (EXIT_SUCCESS);
}
