#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 "fixed_handler.h"

#include <sstream>


using namespace aka2;

void parser_context::report(const std::string &message) const {
  std::cerr << locator_->get_linenumber() 
	    << ':' << locator_->get_columnnumber()
	    << ' ' << message << std::endl;
}

void parser_context::report_wrong_occurence(const std::string &message, 
					    const occurence &occ, int actual) {
  std::ostringstream ostm;
  ostm << message << ' ';
  ostm << "occurence[" << occ.minOccurs_ << ", " << occ.maxOccurs_ << "] "
       << "actual : " << actual;
  report(ostm.rdbuf()->str());
}


void handler::parse_attributes(void *e, const element_op &op, attribute_values &attrs, 
			       const global_attributes &gattrs) {
  
  const attribute_types *atypes = op.get_attribute_types();
  if (atypes == 0) { // No attribute is defined, but found in document.
    if (attrs.size() != 0)
      context_.raise_error("Attribute not declared is found.", __FILE__, __LINE__);
    return;
  }
  
  for (attribute_types::const_iterator it = atypes->begin();
       it != atypes->end(); ++it) {
    
    attribute_values::const_iterator itattr = attrs.find(it->second.get_name().local());
    if (itattr == attrs.end()) {
      if (it->second.is_required()) {
      	std::ostringstream ostm;
	      ostm << "Required attribute(\"" << it->second.get_name().local() << "\") not found.";
	      context_.raise_error(ostm.rdbuf()->str(), __FILE__, __LINE__);
      }
      continue;
    }

    const attribute_type &atype = it->second;
    if (atype.get_schematype() == fixed_id) {
      if (!atype.is_fixed_string(itattr->second)) {
	std::ostringstream ostm;
	ostm << "Fixed attribute(\"" << it->first << "\") has wrong value." << std::endl
	     << "Expected : "; 
	atype.write_fixed_string(ostm);
	ostm << ", Actual : " << itattr->second << std::endl;
	context_.raise_error(ostm.rdbuf()->str(), __FILE__, __LINE__);
      }
    }      
    else {
      it->second.set_member_to_default(e);
      memberpair mpair = it->second.get_member(e);
      assert(mpair.op_.get_schematype() == simpletype_id);
      
      std::istringstream istm(itattr->second);
      static_cast<const simpletype_op&>(mpair.op_).read_text(mpair.e_, istm, gattrs);
    }
    attrs.erase(itattr->first);
  }

  if (!attrs.empty()) {
    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_.raise_error(ostm.rdbuf()->str(), __FILE__, __LINE__);
  }
}


handler* handler::create_particle_handler(const qname &tagname, void *e, const element_op &op,
			     int depth, const occurence &occ) {
  switch (op.get_schematype()) {
  case sequence_id:
  case choice_id:
  case all_id: 
    return create_handler(tagname, e, op, depth, occ, context_);
  case array_id:
  case ptrmember_id:
  case simpletype_id:
  case simplecontent_id:
  case any_id:
  default:
    assert(!"Must not reach here.");
  }
  return 0;
}


handler* handler::create_element_handler(const qname &tagname, void *e, const element_op &op,
			     int depth, const occurence &occ) {
  switch (op.get_schematype()) {
  case simpletype_id:
  case simplecontent_id:
    return create_handler(tagname, e, op, depth, occ, context_);
  case ptrmember_id:
  case any_id:
  case sequence_id:
  case choice_id:
  case all_id: 
  case array_id:
  default:
    assert(!"Must not reach here.");
  }
  return 0;
}



handler* handler::create_root_handler(const qname &tagname, void *e, const element_op &op,
				      const occurence &occ, parser_context &context) {
  switch (op.get_schematype()) {
  case simpletype_id:
  case simplecontent_id:
  case ptrmember_id:
  case any_id:
  case sequence_id:
  case choice_id:
  case all_id: 
    return create_handler(tagname, e, op, 1, occ, context);
  case array_id:
  default:
    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 occurence &occ, 
					     bool is_element) {
  assert(op.get_schematype() == array_id);
  array_handler *h = new array_handler(tagname, e, depth, op, 
				       occ, is_element, context_);
  context_.push(h);
  return h;
}

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




handler* handler::create_handler(const qname &tagname, void *e, const element_op &op,
				 int depth, const occurence &occ, 
				 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), 
			   occ,
			   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 any_id: {
//     h = new any_handler(tagname, *static_cast<any*>(e), depth,
// 			op, context);
//     break;
  }
  case array_id: 
  default:
    assert(!"Must not reach here.");
  }
  context.push(h);
  return h;
}


void parser_context::raise_error(const std::string &message,
				 const char *filename, const unsigned long linenum) {
  std::string error = source_name_ + ':' + locator_->get_position() + ' ' + message;
  throw parse_exception(filename, linenum, error);
}


handler *handler::create_fixed_handler(const qname &tagname, int depth, const member_type &mtype) {
  handler *h = new fixed_handler(tagname, depth, mtype, context_);
  context_.push(h);
  return h;
}

handler *handler::create_fixed_array_handler(const qname &tagname, 
					     int depth, const member_type &mtype) {
  handler *h = new fixed_array_handler(tagname, depth, mtype, context_);
  context_.push(h);
  return h;
}
