#include "sequence_handler.h"

#include "all_handler.h"
#include "choice_handler.h"
#include "simpletype_handler.h"
#include "simplecontent_handler.h"
#include "array_handler.h"
#include "ptrmember_handler.h"
#include "closure_handler.h"
#include "any_handler.h"
#include "fixed_handler.h"
#include <cassert>

using namespace aka2;

sequence_handler::sequence_handler(const qname &tagname, void *seq, int depth,
				   const sequence_op &sop,
				   parser_context &context)  
  : handler(context, depth, tagname) , seq_(seq), sop_(sop) {

  current_ = sop_.get_member_types().begin();
  itend_ = sop_.get_member_types().end();
  
}

sequence_handler::sequence_handler(const qname& tagname, sequence_handler *seqhandler, int depth) 
  : handler(*seqhandler, depth, tagname), 
    seq_(seqhandler->seq_), sop_(seqhandler->sop_) {
  current_ = seqhandler->current_;
  itend_ = seqhandler->itend_;
}


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

  if (current_ == itend_)    // No more element.
    return skip;

  while (current_ != itend_) {

    if (current_->is_element()) {
      bool err = false;

      schematype_id id = current_->get_schematype();
      if (current_->get_name() == tagname)
	return parse_element(tagname, attrs, current_);

      if ((id != array_id) && (id != ptrmember_id))
	err = true;
      if (!can_skip_member())
	err = true;

      if (err) {
	context_.report_no_element(tagname, __FILE__, __LINE__);
	return error;
      }
    }
    else {
      // particle.
      assert(!current_->is_element());
 
      parse_result res = find_particle(tagname, attrs, current_);
      
      if (res == ok)// (Nested) Particle found.
	return ok;
      else if (res == error)  { // Expected element not found. 
	return error;
      }
      assert(res == skip);
    }
    ++current_; // res == skip, then go next.
  }

  // Element not found in particles.
  // Allowing to go to the next particle.
  return skip;
}


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

  member_types::const_iterator it = current_;
  if (it == itend_)    // No more element.
    return skip;

  ++it;

  while (it != itend_) {

    if (it->is_element())
      return it->get_name() == tagname ? ok : error;

    // particle.
    assert(!it->is_element());
    parse_result res = find_particle(tagname, attrs, it);
    if (res == ok)// (Nested) Particle found.
      return ok;
    else if (res == error) { // Expected element not found. 
      return error;
    }
    assert(res == skip);

    ++it;
  }
  
  // Element not found in particles.
  // Allowing to go to the next particle.
  return skip;
}


parse_result sequence_handler::parse_element(const qname &tagname, 
					     const attribute_values &attrs,
					     member_types::const_iterator &it) {
  
  const member_type &mtype = *it;  

  schematype_id id = mtype.get_schematype();  
  cache_.set(seq_, mtype.op(), mtype.getter());
  
  // if @default is specified, copy-construct member.
  cache_.prepare(mtype.is_default_specified());
  void *memelm = cache_.value();
  const element_op &memberop = it->op();

  // Branch according to the member type.
  switch (id) {
  case sequence_id:
  case choice_id:
  case all_id: {
    create_particle_handler(tagname, memelm, memberop, depth_ + 1, 
			    *it);
    break;
  }
  case simpletype_id:
  case simplecontent_id: {
    it->set_member_to_default(seq_);
    create_simple_handler(tagname, memelm, memberop, 
			   depth_ + 1, *it);
    break;
  }
  case fixed_id: { // @fixed is always simpletype. (spec. restriction of akaxiso.)
    // entity of fixed array is null for <xs:sequence>.
    create_fixed_handler(tagname, 0, static_cast<const fixed_op&>(memberop), 
			 depth_ + 1, *it);
    break;
  }
  case array_id: {
    handler *h = create_array_handler(tagname, memelm, 
				      static_cast<const array_op&>(memberop), 
				      depth_, *it, true);
    parse_result res = h->query_element(tagname, attrs);
    assert(res == ok);
    break;
  }
  case ptrmember_id: {
    handler *h = create_ptrmember_handler(tagname, memelm, 
					  static_cast<const ptrmember_op&>(memberop),
					  depth_, *it, true);
    parse_result res = h->query_element(tagname, attrs);
    assert(res == ok);
    break;
  }
  case enclose_id: {
    handler *h = new closure_handler(tagname, this, depth_ + 1);
    context_.push(h);
    ++it;
    break;
  }
  case any_id:       // any is not handled here.
  case any_array_id: // any_array is not handled here.
  case disclose_id:  // disclose does not appear during any startElement() call.
  case any_attribute_id:
    assert(!"Internal error.  Must not reach here.");
  }
return ok;
}



parse_result sequence_handler::find_particle(const qname &tagname, 
					     const attribute_values &attrs,  
					     member_types::const_iterator &it) {

  handler *current = 0;
  const member_type &mtype = *it;

  cache_.set(seq_, mtype.op(), mtype.getter()); 
  cache_.prepare(mtype.is_default_specified());

  void *memelm = cache_.value();
  const element_op &memberop = mtype.op();

  schematype_id id = memberop.get_schematype();

  if ( ((id == any_array_id) || (id == any_id)) 
       && (context_.ignore_any())) // Do not find <xs:any>.
    return error;

  // Branch according to the member type.
  switch (id) {
  case sequence_id:
  case choice_id: 
  case all_id: {
    current = create_particle_handler(tagname, memelm, memberop, 
				      depth_, mtype);
    break;
  }
  case array_id: {
    current = create_array_handler(tagname, memelm, 
				   static_cast<const array_op&>(memberop), 
				   depth_, mtype, false);
    break;
  }
  case ptrmember_id: {
    current = create_ptrmember_handler(tagname, memelm, 
				       static_cast<const ptrmember_op&>(memberop)
				       , depth_, mtype, false);
    break;
  }
  case any_id: { // <xs:any>
    current = create_any_handler(tagname, *static_cast<any*>(memelm), depth_ + 1);
    break;
  }
  case any_array_id: { // <xs:any minOccurs="x" maxOccurs="XX">.....
    current = create_any_array_handler(tagname, *static_cast<any_array*>(memelm),
				       depth_, mtype.get_occurrence());
    break;
  }
  case simpletype_id:    // simpletype is not a particle.
  case simplecontent_id: // simplecontent is not a particle.
  case enclose_id:       // enclose is not a particle.
  case disclose_id:      // disclose is not a particle.
  case any_attribute_id: // attribute is not handled here.
  case fixed_id:         // fixed is always element.(XML-Schema spec. restriction of akaxiso.)
    assert(!"Internal error.  Must not reach here.");
  }

  parse_result res = current->query_element(tagname, attrs);

  if (res == ok)
    return ok;  // Element given by tagname found.

  // Rollup.
  while (context_.top() != current)
    context_.pop();

  // Remove current.
  assert(context_.top() == current);
  context_.pop();

  assert((res == skip) || (res == error));
  cache_.clear();

  if (res == error) {
    context_.report_no_element(tagname, __FILE__, __LINE__);
  }
  return res; // skip or error.
}


parse_result sequence_handler::end_element(const qname &tagname){
  // No element left.  OK to end parse. 
  if (current_ == itend_)
    return ok;

  for (; current_ != itend_; ++current_) {
    // Disclose to terminate sequence handler. 
    if (current_->get_schematype() == disclose_id)
      return ok;

    // If element could not be skipped, it's an error.
    if (!can_skip_member()) {
      std::string msg("Element \"");
      msg += current_->get_name().qualified() + "\" should appear.";
      context_.report(msg, __FILE__, __LINE__);
      return error;
    }
  }
  // All elements are skipped.  OK to terminate sequence handler.
  return skip;
}


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


node sequence_handler::get_node() {
  return node(seq_, sop_);
}


void sequence_handler::receive_child(const node &child) {
  assert(&child.op() == &current_->op());
  cache_.flush();
  ++current_;
}


bool sequence_handler::can_skip_member() {

  // Branch according to the member type.
  handler *current = 0;
  
  switch (current_->get_schematype()) {
  case fixed_id: // fixed is always array.
  case array_id: 
  case any_array_id:
  case ptrmember_id: {
    return current_->get_occurrence().in_range(0);
  }
  case sequence_id:
  case choice_id: 
  case all_id: {
    if (current_->is_element())
      return false;
    current = create_particle_handler(qname(), 0, current_->op(), depth_, *current_);
    break;
  }
  case any_id: // always <xs:element minOccurs="1" maxOccurs="1"/>
  case simpletype_id:
  case simplecontent_id:
  case enclose_id:
  case disclose_id:
    return false;
  case any_attribute_id:
    assert(!"Internal error.  Must not reach here.");
  }

  assert(context_.top() == current);
  bool ok_to_skip = current->can_skip();
  context_.pop();
  return ok_to_skip;
}


bool sequence_handler::can_skip() {

  while (current_ != itend_) {
    bool ok_to_skip = can_skip_member();
    if (!ok_to_skip)
      return false;
    ++current_;
  }
  return true;
}

void sequence_handler::abort() {
  cache_.clear();
}
