//---------------------------------------------------------------------------
//
// 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:     Seminole XML 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"

#include "sitedata.h"

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

class XmlEnhancer : public HttpdHandler
{
  static int Transform(HttpdXmlHost &host, HttpdXmlDomBuilder &document);
  static int Transform(HttpdXmlHost &host, HttpdXmlDomNode *p_node,
                       bool &added);

public:
  XmlEnhancer(const char *p_prefix);
  ~XmlEnhancer();
  virtual bool Handle(HttpdRequest *p_req);
};

//---------------------------------------------------------------------------
//
// NAME:           XmlEnhancer::Transform
//
// ACCESS:         private
//
// DESCRIPTION:    Transform a document tree.
//
// RETURNS:        A status code.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
int XmlEnhancer::Transform(HttpdXmlHost &host, HttpdXmlDomBuilder &document)
{
  HttpdXmlDomNode *p_root = document.Root();
  if (p_root != NULL)
  {
    // Annotate the nodes.
    bool added = false;
    int rc = Transform(host, p_root, added);
    if (rc != 0)
      return (rc);

    // Add a marker at the end of the document indicating the transformation.
    if (added)
    {
      HttpdXmlDomNode *p_note;
      rc = p_root->InsertLastChild(host, "notice", p_note);
      if (rc != 0)
        return (rc);
    
      return (p_note->Body().WriteString("Unit attributes added"));
    }
  }
  
  return (0);
}

//---------------------------------------------------------------------------
//
// NAME:           XmlEnhancer::Transform
//
// ACCESS:         private
//
// DESCRIPTION:    Transform a document node and its children.
//
// RETURNS:        Status code.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
int XmlEnhancer::Transform
 (
   HttpdXmlHost    &host,
   HttpdXmlDomNode *p_node,
   bool            &added
 )
{
  // First -- transform the children.
  HttpdListIterator walk(p_node->Children().Head());
  while (walk.Continue())
  {
    Transform(host, (HttpdXmlDomNode *)(void *)walk, added);
    walk.Next();
  }

  // Is a unit attribute already provided?
  if (HttpdXmlAttribute::FindValue(p_node->Attributes(), "units") != NULL)
    return (0); // Nothing left to go.

  // See if we can figure out the units. 
  const char *p_units = NULL;
  const char *p_tag   = p_node->Tag();
  if (strcmp(p_tag, "temperature") == 0)
    p_units = "degC";
  else if (strcmp(p_tag, "mass") == 0)
    p_units = "kg";
  else if (strcmp(p_tag, "current") == 0)
    p_units = "amps";
  else if (strcmp(p_tag, "potential") == 0)
    p_units = "volts";

  // We figured it out -- add the attribute.
  if (p_units != NULL)
  {
    int rc = p_node->AddAttribute(host, "units", p_units);
    if (rc != 0)
      return (rc);

    added = true;
  }

  return (0);
}

//---------------------------------------------------------------------------
//
// NAME:           XmlEnhancer::XmlEnhancer
//
// DESCRIPTION:    Construct the request handler.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
XmlEnhancer::XmlEnhancer(const char *p_prefix)
{
  mpPrefix = HttpdUtilities::SaveString(p_prefix);
}

//---------------------------------------------------------------------------
//
// NAME:           XmlEnhancer::~XmlEnhancer
//
// DESCRIPTION:    Destroy the request handler.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
XmlEnhancer::~XmlEnhancer()
{
  HttpdOpSys::Free((char *)mpPrefix);
}

//---------------------------------------------------------------------------
//
// NAME:           XmlEnhancer::Handle
//
// DESCRIPTION:    Process the request.
//
// RETURNS:        True if the request was handled by this handler,
//                 false if no processing has been performed.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
bool XmlEnhancer::Handle(HttpdRequest *p_req)
{
  assert(p_req != NULL);
  
  if (IsMe(p_req))
  {
    // This handler only accepts POST requests.
    if (!p_req->IsPostRequest())
    {
      p_req->Respond(HTTPD_RESP_METHOD_NOT_ALLOWED);
      return (true);
    }

    // This handler only accepts XML documents.
    if (!p_req->ContentAvailable("text/xml"))
    {
      p_req->Respond(HTTPD_RESP_BAD_REQ);
      return (true);
    }

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

    rc = document.Create();
    if (rc != 0)
    {
      p_req->Respond(HTTPD_RESP_SRV_ERROR);
      return (true);
    }

    // Parse the request body.
    rc = document.ReadBody(p_req);
    if (rc != 0)
    {
      p_req->Respond(HTTPD_RESP_SRV_ERROR);
      return (true);
    }

    // Transform the document.
    rc = Transform(host, document);
    if (rc != 0)
    {
      p_req->Respond(HTTPD_RESP_SRV_ERROR);
      return (true);
    }

    // Serialize the output.
    HttpdDynamicOutput output(p_req, false);
    p_req->Respond(HTTPD_RESP_OK);
    output.Header("Content-Type", "text/xml");
    output.HeaderComplete();

    HttpdXmlDomWriter dw(output.Body(), 2, 0, 0);
    dw.WriteDom(&document);

    return (true);
  }

  return (false);
}

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

static const char gServername[] = "localhost";
static const int  gServerport   = 8080;
static Httpd      *gpWebServer;

//---------------------------------------------------------------------------
//
// NAME:           failure
//
// DESCRIPTION:    Handle a fatal error.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
static void failure(const char *p_file, int line, const char *p_msg)
{
  assert(p_file != NULL);
  assert(line   >  0);
  assert(p_msg  != NULL);
  
  fprintf(stderr, "%s:%d Call to %s failed\n", p_file, line, p_msg);
  exit(EXIT_FAILURE);
}

//---------------------------------------------------------------------------
//
// NAME:           Initialize_The_Web_Server
//
// DESCRIPTION:    Start and configure Seminole.
//
// AUTHOR:         M. Grosberg
// CREATED:        Jul 22, 2013
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
static void Initialize_The_Web_Server()
{
  //-------------------------------------------------------
  // Initialize the environment.
  //-------------------------------------------------------
  
  if (Httpd::Init() != 0)
    failure(__FILE__, __LINE__, "Httpd::Init()");
  
  //--------------------------------------------------------
  // Create the webserver instance.  This just initializes
  // the object --- it doesn't start it up.
  //--------------------------------------------------------

  gpWebServer = new Httpd(gServername, gServerport);
  if (gpWebServer == NULL)
    failure(__FILE__, __LINE__, "Httpd::Httpd()");

  //--------------------------------------------------------
  // Add the documentation.
  //--------------------------------------------------------

  static HttpdMemoryDataSource builtin(SemWeb, sizeof(SemWeb));
  static HttpdRomFileSystem    romfs;
  if (romfs.Mount(&builtin) != 0)
    failure(__FILE__, __LINE__, "HttpdRomFileSystem::Mount()");
    
  static HttpdFileHandler doc(&romfs, "/", "/");

  gpWebServer->Install(&doc);

  //--------------------------------------------------------
  // Add our handler for manipulating the XML document.
  //--------------------------------------------------------

  static XmlEnhancer hand("/markup");
  gpWebServer->Install(&hand);

  printf("Server started on host %s port %d\n", gServername, gServerport);
}

//---------------------------------------------------------------------------
//
// NAME:           main
//
// DESCRIPTION:    Entry point for the demo.
//
// AUTHOR:         S. Conner
// CREATED:        Mar  6, 2004
// LAST MODIFIED:  N/A           BY:  N/A
//
//--------------------------------------------------------------------------
int main(int argc, const char *const *argv)
{
  char buf[4];

  Initialize_The_Web_Server();
  gpWebServer->Start();
  
  printf("Press <ENTER> to stop.\n");
  fgets(buf, sizeof(buf), stdin);
  
#if HTTPD_INC_SHUTDOWN
  gpWebServer->Stop(HARD);
#endif

  return (EXIT_SUCCESS);
}
