#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 <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) {

  it_ = 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_) {
  it_ = seqhandler->it_;
  itend_ = seqhandler->itend_;
}


parse_result sequence_handler::query_element(const qname &tagname, const attribute_values &attrs, 
				     const global_attributes &gattrs){
  if (it_ == itend_)    // No more element.
    return skip;

  while (it_ != itend_) {

    if (it_->is_element())
      return parse_element(tagname, attrs, gattrs);

    // particle.
    assert(!it_->is_element());
 
    parse_result res = find_particle(tagname, attrs, gattrs);

    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, 
					     const global_attributes &gattrs) {
  
  for (; it_ != itend_; ++it_) {
    schematype_id id = it_->get_schematype();
    if ((id != array_id) && (id != ptrmember_id))
      break;
    if (it_->get_name() == tagname)
      break;
    if (!can_skip_member())
      return error;
  }

  if (it_->get_name() != tagname)
    return error;

  schematype_id id = it_->get_schematype();
  memberpair mpair = it_->get_member(seq_); 
  void *memelm = mpair.e_;
  const element_op *memberop = &mpair.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_->get_occurrence());
    break;
  }
  case simpletype_id:
  case simplecontent_id: {
    it_->set_member_to_default(seq_);
    create_element_handler(tagname, memelm, *memberop, 
			   depth_ + 1, it_->get_occurrence());
    break;
  }
  case fixed_id: {
    handler *h = create_fixed_array_handler(tagname, depth_ + 1, *it_);
    parse_result res = h->query_element(tagname, attrs, gattrs);
    assert(res == ok);
    break;
  }
  case array_id: {
    handler *h = create_array_handler(tagname, memelm, 
				      static_cast<const array_op&>(*memberop), 
				      depth_, it_->get_occurrence(), true);
    parse_result res = h->query_element(tagname, attrs, gattrs);
    assert(res == ok);
    break;
  }
  case ptrmember_id: {
    handler *h = create_ptrmember_handler(tagname, memelm, 
					  static_cast<const ptrmember_op&>(*memberop),
					  depth_, it_->get_occurrence(), true);
    parse_result res = h->query_element(tagname, attrs, gattrs);
    assert(res == ok);
    break;
  }
  case enclose_id: {
    handler *h = new closure_handler(tagname, this, depth_ + 1);
    context_.push(h);
    ++it_;
    break;
  }
  case any_id: 
  case disclose_id:
  default:
    assert(!"Internal error.  Must not reach here.");
  }
  return ok;
}



parse_result sequence_handler::find_particle(const qname &tagname, 
					     const attribute_values &attrs, 
					     const global_attributes &gattrs) {

  // Branch according to the member type.

  handler *current = 0;

  memberpair mpair = it_->get_member(seq_); 
  void *memelm = mpair.e_;
  const element_op &memberop = mpair.op_;

  switch (memberop.get_schematype()) {
  case sequence_id:
  case choice_id: {
    current = create_particle_handler(tagname, memelm, memberop, 
				      depth_, it_->get_occurrence());
    break;
  }
  case array_id: {
    current = create_array_handler(tagname, memelm, 
				   static_cast<const array_op&>(memberop), 
				   depth_, it_->get_occurrence(), false);
    break;
  }
  case ptrmember_id: {
    current = create_ptrmember_handler(tagname, memelm, 
				       static_cast<const ptrmember_op&>(memberop)
				       , depth_, it_->get_occurrence(), false);
    break;
  }
  case all_id: // All could not be in nested particles/groups.
  case simpletype_id:
  case simplecontent_id:
  case enclose_id:
  case disclose_id:
  case any_id:
  default:
    assert(!"Internal error.  Must not reach here.");
  }

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

  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));
  return res; // skip or error.

}


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

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

    // If element could not be skipped, it's an error.
    if (!can_skip_member())
      return error;
  }
  // All elements are skipped.  OK to terminate sequence handler.
  return skip;
}


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


memberpair sequence_handler::get_element() {
  return memberpair(seq_, sop_);
}

void sequence_handler::receive_child(const memberpair &mpair) {
  assert(&mpair.op_ == &it_->get_member(seq_).op_);
  ++it_;
}

bool sequence_handler::can_skip_member() {

  // Branch according to the member type.
  handler *current = 0;
  

  memberpair mpair = it_->get_member(seq_); 
  void *memelm = mpair.e_;
  const element_op &memberop = mpair.op_;

  switch (memberop.get_schematype()) {
  case array_id: 
  case ptrmember_id: {
    return it_->get_occurrence().in_range(0);
  }
  case fixed_id: 
  case sequence_id:
  case choice_id: {
    if (it_->is_element())
      return false;
    current = create_particle_handler(qname(), memelm, mpair.op_, depth_, 
				      it_->get_occurrence());
    break;
  }
  case simpletype_id:
  case simplecontent_id:
  case enclose_id:
  case disclose_id: {
    // Could not be any group.
    return false;
  }
  default:
    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 (it_ != itend_) {
    bool ok_to_skip = can_skip_member();
    if (!ok_to_skip)
      return false;
  }
  return true;
}

