/* -*- c++ -*- */
#include "array_handler.h"

#include "sequence_handler.h"
#include "choice_handler.h"
#include "all_handler.h"
#include "simplecontent_handler.h"
#include "simpletype_handler.h"

#include "../classes/traits.h"
#include "../classes/membertype.h"

#include <cassert>

namespace aka2 {

array_handler::array_handler(const qname &tagname, 
			     void *e,
			     int depth,
			     const array_op &aop, 
			     const element_props &props,
			     parser_context &context) 
  : handler(context, depth, tagname), array_(e), 
    e_(0), aop_(aop), props_(props), count_(0) {
  is_element_ = props.is_element();
}

array_handler::~array_handler() {
  if (e_ != 0)
    abort();
}


parse_result array_handler::query_element(const qname &tagname, const attribute_values &attrs) {

  if (is_element_)
    return parse_element(tagname, attrs);
  else
    return find_particle(tagname, attrs);
}


parse_result array_handler::query_next(const qname &tagname, const attribute_values &attrs) {

  if (is_element_) {
    if (tagname == tagname_)
      return ok;
    return props_.get_occurrence().in_range(count_) ? skip : error;
  }
  else
    return find_particle(tagname, attrs);
}

parse_result array_handler::parse_element(const qname &tagname, const attribute_values &attrs) {

  if (tagname != tagname_) {
    if (props_.get_occurrence().in_range(count_))
      return skip;
    context_.report_wrong_occurrence(props_.get_occurrence(), count_, 
				     __FILE__, __LINE__);
    return error;
  }

  const element_op &op = aop_.get_item_op();
  e_ = op.create();

  switch (op.get_schematype()) {
  case sequence_id:
  case choice_id:
  case all_id: { 
    create_particle_handler(tagname, e_, op, depth_ + 1, props_); // item element.
    return ok;
  }
  case simplecontent_id:
  case simpletype_id: {
    create_simple_handler(tagname, e_, op, depth_ + 1, props_); // item element.
    return ok;
  }
  case fixed_id: {
    create_fixed_handler(tagname, e_, static_cast<const fixed_op&>(op), depth_ + 1, props_);
    return ok;
  }

  case array_id: // nested array is not accepted.
  case ptrmember_id: // ptrmember should not be under array.
  case enclose_id: // The use of enclose/disclose is restricted for <xs:sequence>.
  case disclose_id:
  case any_id: // any, any_array should not be under array.
  case any_array_id:
  case any_attribute_id:  // attribute is not handled here.
    assert(!"Must not reach here.");
  }
  return error;
}

parse_result array_handler::find_particle(const qname &tagname, const attribute_values &attrs) {

  const element_op &op = aop_.get_item_op();
  e_ = op.create();

  handler *handler = 0;

  switch (op.get_schematype()) {
  case sequence_id:
  case choice_id: 
  case all_id: {
    handler = create_particle_handler(tagname, e_, op, depth_, props_);
    parse_result res = handler->query_element(tagname, attrs);
    if (res != ok) {
      while (context_.top() != handler) {
	context_.top()->abort();
	context_.pop();
      }
      context_.top()->abort();
      context_.pop();
    }
    return res;
  }
  case simplecontent_id:
  case simpletype_id:
  case array_id: // nested array does not have any meaning.
  case ptrmember_id: // ptrmember should not be under array.
  case enclose_id: // The use of enclose/disclose is restricted for <xs:sequence>.
  case disclose_id:
  case any_array_id: // any, any_array should not be under array.
  case any_id:
  case fixed_id: // fixed should not be under array.
  case any_attribute_id: // attribute is not handled here.
    assert(!"Must not reach here.");
  }
  return error;
}

parse_result array_handler::end_element(const qname &tagname) {
  if (props_.get_occurrence().in_range(count_))
    return ok;
  context_.report_wrong_occurrence(props_.get_occurrence(), count_,
				   __FILE__, __LINE__);
  return error;
}


bool array_handler::parse_entity(const std::string &entity) {
  return true;
}

node array_handler::get_node() {
  return node(array_, aop_);
}

void array_handler::receive_child(const node &child) {
  assert(&aop_.get_item_op() == &child.op());
  assert(e_ == child.ptr());
  aop_.push(array_, child.ptr());
  aop_.get_item_op().destroy(child.ptr());
  ++count_; // Increase occurrence count.
  e_ = 0;
}

bool array_handler::can_skip() {
  return props_.get_occurrence().in_range(count_);
}

void array_handler::abort() {
  if (e_ != 0) {
    aop_.get_item_op().destroy(e_);
    e_ = 0;
  }
}

} // namespace aka2
