// file      : examples/cxx/parser/wildcard/driver.cxx
// author    : Boris Kolpackov <boris@codesynthesis.com>
// copyright : not copyrighted - public domain

#include <string>
#include <memory>
#include <iostream>

#include "email-pskel.hxx"

using namespace std;
using xml_schema::ro_string;

class binary_pimpl: public email::binary_pskel,
                    public xml_schema::base64_binary_pimpl
{
public:
  virtual void
  name (const string& n)
  {
    cerr << "binary: " << n << endl;
  }

  virtual void
  mime (const string& t)
  {
    cerr << "type:   " << t << endl;
  }

  virtual void
  post_binary ()
  {
    std::auto_ptr<xml_schema::buffer> buf (post_base64_binary ());

    cerr << "size:   " << buf->size () << endl
         << endl;
  }
};

class envelope_pimpl: public email::envelope_pskel
{
public:
  envelope_pimpl (xml_schema::unsigned_int_pskel& uint_p,
                  xml_schema::string_pskel& string_p,
                  email::binary_pskel& binary_p)
      : depth_ (0), cur_ (0),
        uint_p_ (uint_p), string_p_ (string_p), binary_p_ (binary_p)
  {
  }

  virtual void
  to (const string& addr)
  {
    cerr << "To:        " << addr << endl;
  }

  virtual void
  from (const string& addr)
  {
    cerr << "From:      " << addr << endl;
  }

  virtual void
  subject (const string& s)
  {
    cerr << "Subject:   " << s << endl;
  }

  // Wildcard handling. All wildcard events are routed to these
  // four functions. It is our job to dispatch them to the right
  // parsers.
  //
  // If we are not using exceptions or XML Schema validation
  // is enabled then we need to check for the error condition
  // and if present, propagate the error code to the parent
  // parser. The _error_p function returns true if there is
  // an error pending. The _copy_error functions copies the
  // error code to the parent parser.
  //
  //
  virtual void
  _start_any_element (const ro_string& ns, const ro_string& name)
  {
    if (depth_++ > 0)
    {
      // Nested wildcard element.
      //
      if (cur_)
      {
        cur_->_start_element (ns, name);

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (cur_->_error_p ())
          _copy_error (cur_);
#endif
      }
    }
    else
    {
      // Top-level element matched by the any wildcard.
      //

      cur_ = 0;

      if (ns == "http://www.codesynthesis.com/email")
      {
        if (name == "text")
        {
          cur_ = &string_p_;
        }
        else if (name == "binary")
        {
          cur_ = &binary_p_;
        }

        if (cur_ != 0)
        {
          cur_->pre ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
          if (cur_->_error_p ())
          {
            _copy_error (cur_);
            return;
          }
#endif
          cur_->_pre_impl ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
          if (cur_->_error_p ())
            _copy_error (cur_);
#endif

        }
      }

      if (cur_ == 0)
      {
        cerr << "Unknown wildcard content: " << ns << "#" << name << endl;
      }
    }
  }

  virtual void
  _end_any_element (const ro_string& ns, const ro_string& name)
  {
    if (--depth_ > 0)
    {
      if (cur_)
      {
        cur_->_end_element (ns, name);

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (cur_->_error_p ())
          _copy_error (cur_);
#endif
      }
    }
    else
    {
      if (ns == "http://www.codesynthesis.com/email")
      {
        if (name == "text")
        {
          string_p_._post_impl ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
          if (string_p_._error_p ())
          {
            _copy_error (&string_p_);
            return;
          }
#endif
          string text (string_p_.post_string ());

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
          if (string_p_._error_p ())
          {
            _copy_error (&string_p_);
            return;
          }
#endif
          cerr << text << endl
               << endl;
        }
        else if (name == "binary")
        {
          binary_p_._post_impl ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
          if (binary_p_._error_p ())
          {
            _copy_error (&binary_p_);
            return;
          }
#endif

          binary_p_.post_binary ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
          if (binary_p_._error_p ())
          {
            _copy_error (&binary_p_);
            return;
          }
#endif

        }
      }

      cur_ = 0;
    }
  }

  virtual void
  _any_attribute (const ro_string& ns,
                  const ro_string& name,
                  const ro_string& value)
  {
    if (depth_ > 0)
    {
      // Nested wildcard attribute.
      //
      if (cur_)
      {
        cur_->_attribute (ns, name, value);

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (cur_->_error_p ())
          _copy_error (cur_);
#endif
      }
    }
    else
    {
      // Top-level attribute matched by the anyAttribute wildcard.
      //
      if (ns == "http://www.codesynthesis.com/email" && name == "thread-id")
      {
        uint_p_.pre ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (uint_p_._error_p ())
        {
          _copy_error (&uint_p_);
          return;
        }
#endif

        uint_p_._pre_impl ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (uint_p_._error_p ())
        {
          _copy_error (&uint_p_);
          return;
        }
#endif

        uint_p_._characters (value);

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (uint_p_._error_p ())
        {
          _copy_error (&uint_p_);
          return;
        }
#endif

        uint_p_._post_impl ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (uint_p_._error_p ())
        {
          _copy_error (&uint_p_);
          return;
        }
#endif

        unsigned int tid = uint_p_.post_unsigned_int ();

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (uint_p_._error_p ())
        {
          _copy_error (&uint_p_);
          return;
        }
#endif

        cerr << "Thread-id: " << tid << endl;
      }
    }
  }

  virtual void
  _any_characters (const ro_string& s)
  {
    if (depth_ > 0)
    {
      if (cur_)
      {
        cur_->_characters (s);

#if defined(XSDE_VALIDATION_PARSER) || !defined(XSDE_EXCEPTIONS)
        if (cur_->_error_p ())
          _copy_error (cur_);
#endif
      }
    }
  }

private:
  std::size_t depth_;
  xml_schema::parser_base* cur_;

  // Parsers for the unsigned int, string and binary types.
  //
private:
  xml_schema::unsigned_int_pskel& uint_p_;
  xml_schema::string_pskel& string_p_;
  email::binary_pskel& binary_p_;
};


int
main (int argc, char* argv[])
{
  if (argc != 2)
  {
    cerr << "usage: " << argv[0] << " hello.xml" << endl;
    return 1;
  }

  try
  {
    // Construct the parser.
    //
    xml_schema::unsigned_int_pimpl unsigned_int_p;
    xml_schema::string_pimpl string_p;
    binary_pimpl binary_p;
    envelope_pimpl envelope_p (unsigned_int_p, string_p, binary_p);

    binary_p.parsers (string_p,  // name
                      string_p); // mime

    envelope_p.parsers (string_p,  // to
                        string_p,  // from
                        string_p); // subject

    // Parse the XML instance document.
    //
    xml_schema::document_pimpl doc_p (
      envelope_p,
      "http://www.codesynthesis.com/email",
      "message");

    envelope_p.pre ();
    doc_p.parse (argv[1]);
    envelope_p.post_envelope ();
  }
  catch (const xml_schema::parser_exception& e)
  {
    cerr << argv[1] << ":" << e.line () << ":" << e.column () << ": "
         << e.text () << endl;
    return 1;
  }
  catch (const std::ios_base::failure&)
  {
    cerr << argv[1] << ": unable to open or read failure" << endl;
    return 1;
  }
}
