#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(global_attributes &gattrs) : context_(gattrs), 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);
    node root = doc_.get_root();
    const element_op &op = doc_.get_op();
    handler *h = 0;

    switch (op.get_schematype()) {
    case sequence_id:
    case choice_id: 
    case all_id:
    case simplecontent_id:
    case simpletype_id:
    case fixed_id: { 
      h = handler::create_handler(tagname, root.ptr(), op, 1, doc_.get_props(), context_);
      break;
    }
    case any_id: // appears under particles.
    case array_id: 
    case ptrmember_id:
    case enclose_id:
    case disclose_id:
    case any_array_id:
    case any_attribute_id:
      assert(!"Must not reach here.");
    }

    h->parse_attributes(root.ptr(), op, attrs);
    return;
  }

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

    handler_ptr currenthandler = context_.top();
    
    parse_result res = currenthandler->query_element(tagname, attrs);
    if (res == ok) {
      node mpair = context_.top()->get_node();
      currenthandler->parse_attributes(mpair.ptr(), mpair.op(), attrs);
      return;
    }

    if (res == error)
      context_.report("Error found.", __FILE__, __LINE__);

    assert(res == skip);
    if (go_up(tagname) == error)
      break;
    if (context_.empty())
      break;
  }
  context_.report_no_element(tagname, __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_); 
  if (!res)
    context_.report("Failed to parse entity.", __FILE__, __LINE__);
  
  while (depth_ != context_.top()->get_depth()) {
    assert(depth_ < context_.top()->get_depth());
    if (go_up(tagname) == error) {
      context_.report(std::string("Failed to end element(") + tagname.qualified() + ").", 
		      __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_node());
  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) {
  context_.gattrs().associate_namespace_prefix(prefix, uri);
}

void document_handler::end_prefix_mapping(const std::string &prefix) {
  context_.gattrs().clear_namespace_prefix(prefix);
}

