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

#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 "any_array_handler.h"
#include "fixed_handler.h"
#include "../classes/member_cache.h"

#include <iostream>
#include <sstream>


using namespace aka2;

parser_context::~parser_context() { 
  while (!handlers_.empty()) {
    handlers_.top()->abort();
    handlers_.pop();
  }
  delete locator_; 
}


void parser_context::report(const std::string &message,
			    const char *filename, const unsigned long linenum) const {
//   std::cerr << locator_->get_linenumber() 
// 	    << ':' << locator_->get_columnnumber()
// 	    << ' ' << message << std::endl;
  if (silent_)
    return;
  std::string error = source_name_ + ':' + locator_->get_position() + ' ' + message;
  throw parse_error(error, filename, linenum);
}


void parser_context::report_wrong_occurrence(const occurrence &occ, int actual,
					     const char *filename, 
					     const unsigned long linenum) const {
  if (silent_)
    return;

  std::ostringstream ostm;
  ostm << "Wrong occurrence: expected[" << occ.minOccurs_ << ", " << occ.maxOccurs_ << "] "
       << "actual : " << actual;
  report(ostm.rdbuf()->str(), filename, linenum);
}


void parser_context::report_no_element(const qname &tagname,
				       const char *filename, const unsigned long linenum) const {
  if (silent_)
    return;

  std::string str("Element(");
  str += tagname.qualified();
  str += ") not defined.";
  report(str, filename, linenum);
}


void handler::parse_attributes(void *e, const element_op &op, attribute_values &attrs) {
  
  const attribute_types *atypes = op.get_attribute_types();
  const any_member *anytypes = op.get_anyattr_type();

  if ((atypes == 0) && (anytypes == 0)) { 
    // No attribute is defined, but found in document.
    if (attrs.size() != 0)
      context_.report("Attribute is not declared in xiso.", __FILE__, __LINE__);
    return;
  }
  
  if (atypes != 0) {

    for (attribute_types::const_iterator it = atypes->begin();
	 it != atypes->end(); ++it) {
    
      attribute_values::const_iterator itattr = attrs.find(it->get_name());
      if (itattr == attrs.end()) {
	if (it->is_required()) {
	  std::ostringstream ostm;
	  ostm << "Required attribute(\"" << it->get_name() << "\") not found.";
	  context_.report(ostm.rdbuf()->str(), __FILE__, __LINE__);
	}
	continue;
      }
    
      const attribute_type &atype = *it;
      if (atype.get_schematype() == fixed_id) {
	if (!atype.is_fixed_string(itattr->second, context_.get_preconditions())) {
	  std::ostringstream ostm;
	  ostm << "Fixed attribute(\"" << it->get_name() << "\") has wrong value." << std::endl
	       << "Expected : "; 
	  atype.write_fixed_string(ostm, context_.get_preconditions());
	  ostm << ", Actual : " << itattr->second << std::endl;
	  context_.report(ostm.rdbuf()->str(), __FILE__, __LINE__);
	}
      }      
      else {
	const named_member &mtype = *it;
	assert(mtype.get_schematype() == simpletype_id);
	mtype.set_member_to_default(e);
	member_cache cache(e, mtype.op(), mtype.getter());

	cache.prepare(true);
	std::istringstream istm(itattr->second);

	try {
	  static_cast<const simpletype_op&>(mtype.op()).
	    read_text(cache.value(), istm, context_.get_preconditions());
	}
	catch ( const std::exception &e ) {
	  context_.report(e.what(), __FILE__, __LINE__);
	}
	cache.flush();
      }
      attrs.erase(itattr->first);
    }

    if (attrs.empty())
      return;
  } 
  

  if (anytypes == 0) { 
    std::ostringstream ostm;
    ostm << "Attribute(s) ";
    for (attribute_values::iterator it = attrs.begin(); it != attrs.end(); ++it) {
      ostm << "\"" << it->first << "\" ";
    }
    ostm << "appear(s) in the document, but not defined in xiso.";
    context_.report(ostm.rdbuf()->str(), __FILE__, __LINE__);
    return;
  }

  member_cache cache(e, anytypes->op(), anytypes->getter());
  cache.prepare(false);
  any_attributes &wcattrs = *static_cast<any_attributes*>(cache.value());
  assert(&wcattrs != 0);
  
  for (attribute_values::const_iterator anyit = attrs.begin();
       anyit != attrs.end(); ++anyit) {
    any_attribute wcattr;
    wcattr.name_ = anyit->first;
    wcattr.value_ = anyit->second;
    wcattrs.push_back(wcattr);
  }
  cache.flush();
}


handler* handler::create_particle_handler(const qname &tagname, void *e, const element_op &op,
			     int depth, const element_props &props) {
  switch (op.get_schematype()) {
  case sequence_id:
  case choice_id:
  case all_id: 
    return create_handler(tagname, e, op, depth, props, context_);
  case array_id: // They are not particle.
  case ptrmember_id:
  case simpletype_id:
  case simplecontent_id:
  case any_id:
  case any_array_id:
  case enclose_id:
  case disclose_id:
  case fixed_id:
  case any_attribute_id:
    assert(!"Must not reach here.");
  }
  return 0;
}


handler* handler::create_simple_handler(const qname &tagname, void *e, const element_op &op,
			     int depth, const element_props &props) {
  // This is for xs:simpleType/xs:simpleContent.
  switch (op.get_schematype()) {
  case simpletype_id:
  case simplecontent_id:
  case fixed_id:
    return create_handler(tagname, e, op, depth, props, context_);
  case ptrmember_id:
  case any_id:
  case any_array_id:
  case sequence_id:
  case choice_id:
  case all_id: 
  case array_id:
  case enclose_id:
  case disclose_id:
  case any_attribute_id:
    assert(!"Must not reach here.");
  }
  return 0;
}




array_handler* handler::create_array_handler(const qname &tagname, void *e, 
					     const array_op &op,
					     int depth, const element_props &props) {
  assert(op.get_schematype() == array_id);
  array_handler *h = new array_handler(tagname, e, depth, op, 
				       props, context_);
  context_.push(h);
  return h;
}

ptrmember_handler* handler::create_ptrmember_handler(const qname &tagname, 
						     void *e, const ptrmember_op &op,
						     int depth, const element_props &props) {
  assert(op.get_schematype() == ptrmember_id);
  ptrmember_handler *h = new ptrmember_handler(tagname, e, depth, op, 
					       props, context_);
  context_.push(h);
  return h;
}


handler* handler::create_handler(const qname &tagname, void *e, const element_op &op,
				 int depth, const element_props &props, 
				 parser_context &context) {

  handler *h;
  
  switch (op.get_schematype()) {
  case sequence_id:
    h = new sequence_handler(tagname, e, depth, static_cast<const sequence_op&>(op), 
			     context);
    break;
  case choice_id: 
    h = new choice_handler(tagname, e, depth, 
			   static_cast<const choice_op&>(op), 
			   props.get_occurrence(),
			   context);
    break;
  case all_id:
    h = new all_handler(tagname, e, depth,
			static_cast<const all_op&>(op),
			context);
    break;
  case simplecontent_id:
    h = new simplecontent_handler(tagname, e, depth, 
				  static_cast<const simplecontent_op&>(op), 
				  context);
    break;
  case simpletype_id:
    h = new simpletype_handler(tagname, e, depth, 
			       static_cast<const simpletype_op&>(op), 
			       context);
    break;
  case fixed_id:
    h = new fixed_handler(tagname, e, static_cast<const fixed_op&>(op), depth, props, context);
    break;
  case any_id:
    h = new any_handler(tagname, *static_cast<any*>(e), depth, context);
    break;
  case array_id: 
    h = new array_handler(tagname, e, depth, static_cast<const array_op&>(op), props, 
			  context);
    break;
  case any_array_id:
  case ptrmember_id:
  case enclose_id:
  case disclose_id:
  case any_attribute_id:
    assert(!"Must not reach here.");
  }
  context.push(h);
  return h;
}

fixed_handler *handler::create_fixed_handler(const qname &tagname,
					     void *e,
					     const fixed_op &fop,
					     int depth,
					     const element_props &props) {
  fixed_handler *h = new fixed_handler(tagname, e, fop, depth, props, context_);
  context_.push(h);
  return h;
}


handler *handler::create_any_handler(const qname &tagname, any &an, int depth) {
  handler *h = new any_handler(tagname, an, depth, context_);
  context_.push(h);
  return h;
}

handler *handler::create_any_array_handler(const qname &tagname, any_array &anys, 
					   int depth, const element_props &props) {
  handler *h = new any_array_handler(tagname, anys, depth, props, context_);
  context_.push(h);
  return h;
}


bool handler::seek_forward(const qname &tagname, const attribute_values &attrs) {
  context_.set_ignore_any(true);

  handler_stack saved = context_.get_stack();
  
  context_.silent(true);
  context_.pop(); // pop myself.
  handler *h;

  bool found = false;

  if (context_.empty()) {
    found = false;
  }
  else {
    try {
      do {
	h = context_.top().get();
	parse_result res = h->query_next(tagname, attrs);
	
	if (res == ok) { // Matched element found.
	  found = true;
	  break;
	}
	else if (res == error) {// Cannot found matched one.
	  found = false;
	  break;
	}
	assert(res == skip);
	context_.pop();
	
      } while ((!context_.empty()) && (h->get_depth() >= depth_));
    }
    catch ( ... ) {
      assert(!"Exception must not be thrown.");
    }
  }
  context_.set_stack(saved);
  context_.silent(false);
  context_.set_ignore_any(false);
  return found;
}
