/* -*- c++ -*- */
#include "ptrmember_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 {

ptrmember_handler::ptrmember_handler(const qname &tagname, 
				     void *e,
				     int depth,
				     const ptrmember_op &pop, 
				     const element_props &props,
				     bool is_element,
				     parser_context &context) 
  : handler(context, depth, tagname), ptrmember_(e), 
    e_(0), pop_(pop), props_(props), is_element_(is_element), appeared_(false) {
}

ptrmember_handler::~ptrmember_handler() {
}


parse_result ptrmember_handler::query_element(const qname &tagname, const attribute_values &attrs) {
  if (appeared_)
    return skip;
  
  if (is_element_)
    return parse_element(tagname, attrs);
  else
    return find_particle(tagname, attrs);
}


parse_result ptrmember_handler::query_next(const qname &tagname, const attribute_values &attrs) {
  
  if (is_element_) {
    if (appeared_)
      return skip; 
    if (tagname != tagname_)
      return props_.get_occurrence().in_range(0) ? skip : error; 
    return ok;
  }
  else {
    parse_result res = find_particle(tagname, attrs);
    pop_.destroy(ptrmember_);
    e_ = 0;
    return res;
  }
}

parse_result ptrmember_handler::parse_element(const qname &tagname, const attribute_values &attrs) {
  if (appeared_)
    return skip;

  if (tagname != tagname_) {
    if (props_.get_occurrence().in_range(0))
      return skip;
    context_.report_wrong_occurrence(props_.get_occurrence(), 0, __FILE__, __LINE__);
    return error;
  }
  const element_op &op = pop_.get_value_op();
  e_ = pop_.create_member(ptrmember_);

  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 any_id: {
    create_any_handler(tagname, *static_cast<any*>(e_), depth_ + 1);
    return ok;
  }
  case array_id: // array/ptrmember should not be under the ptrmember.
  case ptrmember_id:
  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 ptrmember.
  case fixed_id: // fixed_id should not be under the ptrmember. ( Because it is an array. )
  case any_attribute_id: // attribute is not handled here.
    assert(!"Must not reach here.");
  }
  return error;
}

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

  e_ = pop_.create_member(ptrmember_); 
  const element_op &op = pop_.get_value_op();

  handler *handler = 0;

  schematype_id id = op.get_schematype();

  if ((id == any_id) && context_.ignore_any())
    return error;

  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)
      return ok;
    
    while (context_.top() != handler) {
      context_.top()->abort();
      context_.pop();
    }
    return props_.get_occurrence().in_range(0) ? skip : error;
  }
  case any_id: { // <xs:any>
    bool next_found = seek_forward(tagname, attrs);
    if (!next_found) {
      create_any_handler(tagname, *static_cast<any*>(e_), depth_ + 1);
      return ok;
    }
    return props_.get_occurrence().in_range(0) ? skip : error;
  }
  case simplecontent_id: // simpletype, simplecontent could not be particle.
  case simpletype_id:
  case any_array_id:
  case array_id: // array should not be under ptrmember.
  case ptrmember_id: // nested ptrmember has no meaning.
  case enclose_id: // The use of enclose/disclose is restricted for ptrmember.
  case disclose_id:
  case fixed_id: // fixed should not be particle.
  case any_attribute_id: // attribute is not handled here.
    assert(!"Must not reach here.");
  }
  return error;
}

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


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

node ptrmember_handler::get_node() {
  return node(ptrmember_, pop_);
}

void ptrmember_handler::receive_child(const node &child) {
  assert(&pop_.get_value_op() == &child.op());
  assert(e_ == child.ptr());
  appeared_ = true;
  e_ = 0;
}

bool ptrmember_handler::can_skip() {
  return props_.get_occurrence().in_range(0);
}

void ptrmember_handler::abort() {
  e_ = 0;
}

} // namespace aka2
