#include "document_handler.h"

#include "../classes/qname.h"
#include "../classes/document.h"
#include "sequence_handler.h"
#include "choice_handler.h"
#include "all_handler.h"
#include "simplecontent_handler.h"
#include "array_handler.h"
#include "simpletype_handler.h"
#include "any_handler.h"

using namespace aka2;

document_handler::document_handler() : gattrs_(&default_attrs_), depth_(0) {
}

document_handler::~document_handler() {
}


void document_handler::startElement(const qname &tagname , attribute_values& attrs) {

  ++depth_;

  if (context_.empty()) {
    assert(depth_ == 1);

    charsbuf_.resize(0);
    doc_ = system_document_factory().create_named_document(tagname);
    void *root = doc_.get_root();
    const element_op &op = doc_.get_op();
    handler *handler = 0;

    switch (op.get_schematype()) {
    case sequence_id:
    case choice_id: 
    case all_id:
    case simplecontent_id:
    case simpletype_id:
    case any_id:
      handler = handler::create_root_handler(tagname, root, op, 
					     doc_.get_occurrence(),
					     context_);
      break;
    case array_id: // Number of the root node must be 1.
    default:
      assert(!"Must not reach here.");
    }
    handler->parse_attributes(root, op, attrs, *gattrs_);
    return;
  }

  while ((depth_ - 1) <= context_.top()->get_depth()) {
      
    charsbuf_.resize(0);
    context_.top()->parse_entity(charsbuf_, *gattrs_);

    handler_ptr currenthandler = context_.top();
    
    parse_result res = currenthandler->query_element(tagname, attrs, *gattrs_);
    if (res == ok) {
      memberpair mpair = context_.top()->get_element();
      currenthandler->parse_attributes(mpair.e_, mpair.op_, attrs, *gattrs_);
      return;
    }
    if (res == error)
      context_.raise_error("Error found.", __FILE__, __LINE__);
    assert(res == skip);
    if (go_up(tagname) == error)
      break;
  }
  context_.raise_error("Element not found.", __FILE__, __LINE__);
}


void document_handler::endElement(const qname &tagname){
  assert(depth_ > 0);
  --depth_;

  assert(!context_.empty());
  handler_ptr handler = context_.top();
  bool res = handler->parse_entity(charsbuf_, *gattrs_); 
  if (!res)
    context_.raise_error("Failed to end element.", __FILE__, __LINE__);
  
  while (depth_ != context_.top()->get_depth()) {
    assert(depth_ < context_.top()->get_depth());
    if (go_up(tagname) == error)
      context_.raise_error("Failed to end element during roll_up.", __FILE__, __LINE__);
    if (context_.empty())
      break;
  }

  charsbuf_.resize(0);

}

parse_result document_handler::go_up(const qname &tagname) {

  handler_ptr h = context_.top();
  context_.pop();

  parse_result res = h->end_element(tagname);
  if (res == error)
    h->abort();
  else if (!context_.empty()) // skip or ok.
    context_.top()->receive_child(h->get_element());
  return res;
}


void document_handler::characters(const   char*     chars, 
				  const unsigned int    length){
  charsbuf_ += std::string(chars, length);
}


void document_handler::set_locator(const locator* locator) {
  context_.set_locator(locator);
}

void document_handler::start_prefix_mapping(const std::string &prefix, const std::string &uri) {
  gattrs_->associate_namespace_prefix(prefix, uri);
}

void document_handler::end_prefix_mapping(const std::string &prefix) {
  gattrs_->clear_namespace_prefix(prefix);
}

