#include "processor.h"
#include "name_resolver.h"
#include "exception.h"
#include <algorithm>
#include "xschemaparser.h"
#include "generator.h"



/** processor class implementation */

bool processor::is_form_qualified(const std::string &val) {
  if (val.find("unqualified") != std::string::npos)
    return false;
  if (val.find("qualified") != std::string::npos)
    return true;

  throw fatal_error(val + " does not specify form(qualified/unqualified).", __FILE__, __LINE__);
  return false;
}

aka::qname processor::create_attribute_name(const aka::qname &name, 
					    const std::string &form) {

  bool qualified;
  if (!form.empty())
    qualified = is_form_qualified(form);
  else if (attribute_qualified_)
    qualified = true;

  if (qualified)
    return name;

  if (name.get_uri_id() == target_ns_id_)
    return name.local();
  else
    return name;
}


aka::qname processor::create_element_name(const aka::qname &name, 
					  const std::string &form) {
  bool qualified;
  if (!form.empty())
    qualified = is_form_qualified(form);
  else if (element_qualified_)
    qualified = true;

  if (qualified)
    return name;

  if (name.get_uri_id() == target_ns_id_)
    return name.local();
  else
    return name;
}


aka::qname processor::create_anonymous_name(const aka::qname &name, const std::string &postfix,
					    size_t index) {
  std::ostringstream ostm;
  if (!name.empty())
    ostm << name << '_';
  ostm << postfix << index;
  return aka::qname(ostm.rdbuf()->str());
}


bool processor::is_attrDeclsEmpty(const xs::attrDecls &attrDecls) {
  return attrDecls.choice_.empty() && (attrDecls.anyAttribute_.get() == 0);
}


void processor::preprocess(const xs::schema &schema) {

  target_ns_id_ = aka::get_uri_id(schema.anyURI_);

  attribute_qualified_ = is_form_qualified(schema.attributeFormDefault_);
  element_qualified_ = is_form_qualified(schema.elementFormDefault_);

  /** targetNamespace */
  const xs::directives &dirs = schema.directives_;
  for (xs::directives::const_iterator it = dirs.begin();
       it != dirs.end(); ++it) {
    const aka::item &item = *it;
    const aka::qname &tagname = item.get_name();

    if (tagname == aka::qname("xs:include")) {
      process_include(aka::item_cast<xs::_include>(item));
    }
    else if (tagname == aka::qname("xs:import")) {
      process_import(aka::item_cast<xs::import>(item));
    }
    else if (tagname == aka::qname("xs:redefine")) {
      process_redefine(aka::item_cast<xs::redefine>(item));
    }
    else if (tagname == aka::qname("xs:annotation")) {
      // Nothing to do.
    }
  }

  registry_.ns_order_.push_back(target_ns_id_);
  // finalDefault, blockDefault, id, xml:lang, version is not processed. !!!!!

}

void processor::process_include(const xs::_include &include) {
  xschemaparser parser(ostm_, verbose_, false);
  xs::schema *schema = parser.parse(basepath_ + include.schemaLocation_);
  if (schema == 0)
    throw fatal_error(include.schemaLocation_ + ": Failed to include.", __FILE__, __LINE__);
  processor pr(registry_, basepath_, ostm_, verbose_);
  aka::scoped_ptr<xs::schema> sc(schema);
  pr.preprocess(*sc);
  if (pr.get_target_ns_id() != target_ns_id_)
    throw fatal_error(include.schemaLocation_ + ": Included document has different namespace.",
		      __FILE__, __LINE__); // The included NS differs from the target one.
  pr.process(*sc);
  sc.reset();
}

void processor::process_import(const xs::import &import) {
  xschemaparser parser(ostm_, verbose_, false);
  xs::schema *schema = parser.parse(basepath_ + import.schemaLocation_);
  if (schema == 0)
    throw fatal_error(import.schemaLocation_ + " : Failed to import.", __FILE__, __LINE__);
  processor pr(registry_, basepath_, ostm_, verbose_);
  aka::scoped_ptr<xs::schema> sc(schema);
  pr.preprocess(*sc);
  if (pr.get_target_ns_id() == target_ns_id_)
    throw fatal_error(import.schemaLocation_ + 
		      " : namespace of imported document should not be the same.", 
		      __FILE__, __LINE__); // Importing NS is the same as the target schema doc. 
  pr.process(*sc);
  sc.reset();

}

void processor::process_redefine(const xs::redefine &redefine) {
  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  throw not_implemented(__FILE__, __LINE__);
}


void processor::scan_topLevelTypes(const xs::schema &schema) {

  const xs::annotatedSchemaTops &tops = schema.annotatedSchemaTops_;
  for (xs::annotatedSchemaTops::const_iterator it = tops.begin(); it != tops.end(); ++it) {
    const xs::schemaTop &top = it->schemaTop_;

    for (xs::schemaTop::const_iterator tit = top.begin(); tit != top.end(); ++tit) {
      const aka::qname &name = tit->get_name();
      if (name == aka::qname("&redefinable")) {
	const xs::redefinable &redefinable = aka::item_cast<xs::redefinable>(*tit);
	process_redefinable(redefinable);
      }
      else if (name == aka::qname("xs:element")) {
	const xs::topLevelElement &el = 
	  aka::item_cast<const xs::topLevelElement>(*tit);
	aka::qname name(target_ns_id_, el.name_);
	topLevelElements_.add(name, &el);
      }
      else if (name == aka::qname("xs:attribute")) {
	const xs::topLevelAttribute &attr = aka::item_cast<const xs::topLevelAttribute>(*tit);
	aka::qname name(target_ns_id_, attr.name_);
	topLevelAttributes_.add(name, &attr);
      }
      else if (name == aka::qname("xs:notation")) {
	// Nothing to do here.
      }
      else {
	assert(!"Must not reach here.");
      }
    }
  }
}


void processor::process(const xs::schema &schema) {

  registry_.insert_simpleTypes();

  scan_topLevelTypes(schema);

  {
    for (topLevelSimpleTypes::iterator it = topLevelSimpleTypes_.begin();
	 it != topLevelSimpleTypes_.end(); ++it) {
      process_topLevelSimpleType(it->second.name_, it->second.t_);
      registry_.toplevels_.insert(it->second.name_);
    }
  }

  {
    for (topLevelAttributes::iterator it = topLevelAttributes_.begin();
	 it != topLevelAttributes_.end(); ++it) {
      process_topLevelAttribute(it->second.name_, *it->second.t_);
    }
  }

  {
    for (topLevelAttributeGroups::iterator it = topLevelAttributeGroups_.begin();
	 it != topLevelAttributeGroups_.end(); ++it) {
      process_topLevelAttributeGroup(it->second.name_, it->second.t_);
    }
  }

  {
    for (topLevelElements::iterator it = topLevelElements_.begin();
	 it != topLevelElements_.end(); ++it) {
      process_topLevelElement(it->second.name_, it->second.t_); 
    }
  }
  
  {
    for (topLevelComplexTypes::iterator it = topLevelComplexTypes_.begin();
	 it != topLevelComplexTypes_.end(); ++it) {
      process_topLevelComplexType(it->second.name_, it->second.t_);
      registry_.toplevels_.insert(it->second.name_);
    }
  }

  {
    for (namedGroups::iterator it = namedGroups_.begin();
	 it != namedGroups_.end(); ++it) {
      process_namedGroup(it->second.name_, it->second.t_);
      registry_.toplevels_.insert(it->second.name_);
    }
  }

  {
    for (topLevelElements::iterator it = topLevelElements_.begin();
	 it != topLevelElements_.end(); ++it) {
      check_reference(*it->second.t_); 
    }
  }

}

aka::qname processor::process_topLevelElement(const aka::qname &name, 
					      const xs::topLevelElement *el) {

  if (registry_.elements_.exists(name))
    return registry_.elements_.resolve(name);

  if (verbose_)
    ostm_ << "processing <xs:element name=\"" << name << "\"/>" << std::endl;

  el = &topLevelElements_.get(name);

  if (!el->substitutionGroup_.empty() || !el->block_.empty())
    throw not_supported(__FILE__, __LINE__);

  aka::qname type;

  if (el->abstract_) {
    throw not_supported(__FILE__, __LINE__); // not supported.
    return aka::qname();
  }

  if (!el->type_.empty()) {
    if (!el->typeChoice_.empty())
      throw fatal_error();
    type = el->type_;
  }
  else if (!el->typeChoice_.empty()) {
    assert(el->type_.empty());// Must satisfy.
    type = process_typeChoice(name, el->typeChoice_);
    registry_.toplevels_.insert(type);
  }
  else {
    /** empty <xs:element name="foo"/> */
    type = aka::qname("aka:nill");
  }
  assert(!type.empty());
  registry_.elements_.add(name, type);

  return type;
}

void processor::check_reference(const xs::topLevelElement &el) {
  if (el.type_.empty())
    return;
  if (registry_.is_predefined(el.type_))
    return;
  if (registry_.simpleTypes_.exists(el.type_))
    return;
  topLevelComplexTypes_.get(el.type_);
}

void processor::process_redefinable(const xs::redefinable &redefinable) {

  for (xs::redefinable::const_iterator rit = redefinable.begin();
       rit != redefinable.end(); ++rit) {
    const aka::qname &redefname = rit->get_name();
    if (redefname == aka::qname("xs:simpleType")) {
      const xs::topLevelSimpleType &st = 
	aka::item_cast<const xs::topLevelSimpleType>(*rit);
      aka::qname stname(target_ns_id_, st.name_);
      topLevelSimpleTypes_.add(stname, &st);
    }
    else if (redefname == aka::qname("xs:complexType")) {
      const xs::topLevelComplexType &ct = 
	aka::item_cast<const xs::topLevelComplexType>(*rit);
      aka::qname ctname(target_ns_id_, ct.name_);
      topLevelComplexTypes_.add(ctname, &ct);
    }
    else if (redefname == aka::qname("xs:group")) {
      const xs::namedGroup &gr = aka::item_cast<const xs::namedGroup>(*rit);
      aka::qname name(target_ns_id_, gr.name_);
      namedGroups_.add(name, &gr);
    }
    else if (redefname == aka::qname("xs:attributeGroup")) {
      const xs::namedAttributeGroup &ag = 
	aka::item_cast<const xs::namedAttributeGroup>(*rit);
      aka::qname name(target_ns_id_, ag.name_);
      topLevelAttributeGroups_.add(name, &ag);
    }
    else {
      assert(!"Must not reach here.");
    }
  }
}


void processor::process_topLevelAttribute(const aka::qname &name, 
					  const xs::topLevelAttribute &tat) {

  if (registry_.attributes_.exists(name))
    return;

  if (verbose_)
    ostm_ << "processing <xs:attribute name=\"" << name << "\"/>" << std::endl;

  aka::qname type;
  if (!tat.type_.empty()) {
    if (tat.simpleType_.get() != 0)
      throw fatal_error();
    type = tat.type_;
  }
  else if (tat.simpleType_.get() != 0) {
    assert(tat.type_.empty());
    aka::qname type = process_localSimpleType(name, *tat.simpleType_.get());
    registry_.toplevels_.insert(type);
  }
  registry_.attributes_.add(name, type);
}

void processor::process_topLevelSimpleType(const aka::qname &name, 
					   const xs::topLevelSimpleType *st) {
  if (registry_.is_predefined(name))
    return;

  if (registry_.classes_.exists(name))
    return;

  if (verbose_)
    ostm_ << "processing <xs:simpleType name=\"" << name << "\"/>" << std::endl;

  // Check restriction/extension/union.... !!!!!!!!

  st = &topLevelSimpleTypes_.get(name);
  aka::qname type = process_simpleDerivation(st->simpleDerivation_);

  class_def *def = new simpletype_def(name);
  def->value_type_ = type;
  registry_.classes_.insert(name, def);
  registry_.simpleTypes_.add(name); 
}

const class_def &processor::process_topLevelComplexType(const aka::qname &name, 
							const xs::topLevelComplexType *ct) {

  if (complexType_processing_.find(name) != complexType_processing_.end())
    throw fatal_error(); // cyclic processing.

  if (registry_.type_exists(name))
    return *registry_.classes_.get(name);

  complexType_processing_.insert(name);

  if (ct == 0)
    ct = &topLevelComplexTypes_.get(name);
  if (verbose_)
    ostm_ << "processing <xs:complexType name=\"" << name << "\"/>" << std::endl;

  const class_def &def = process_complexTypeModel(name, ct->model_);

  complexType_processing_.erase(name);

  return def;
}

void processor::process_topLevelAttributeGroup(const aka::qname &name, 
					       const xs::namedAttributeGroup *ag) {
  if (attrG_processing_.find(name) != attrG_processing_.end())
    throw fatal_error();

  if (registry_.attributeGroups_.exists(name))
    return;

  attrG_processing_.insert(name);

  if (ag == 0)
    ag = &topLevelAttributeGroups_.get(name);

  if (verbose_)
    ostm_ << "processing <xs:attributeGroup name=\"" << name << "\"/>" << std::endl;

  attributeGroup_def def;
  def.name_ = name;
  process_attrDecls(name, def, ag->attrDecls_);
  registry_.attributeGroups_.insert(def.name_, def);
  attrG_processing_.erase(name);
}


void processor::process_attrDecls(const aka::qname &hint,
				  attribute_defs &attrdefs, const xs::attrDecls &decls) {
  
  for (xs::attributeChoice::const_iterator ait = decls.choice_.begin();
       ait != decls.choice_.end(); ++ait) {
    if (ait->get_name() == aka::qname("xs:attribute")) {
      const xs::attribute &attr = aka::item_cast<xs::attribute>(*ait);
      process_attribute(hint, attrdefs, attr);
    }
    else if (ait->get_name() == aka::qname("xs:attributeGroup")) {
      const xs::attributeGroupRef &attrGroupRef =
	aka::item_cast<const xs::attributeGroupRef>(*ait);
      process_topLevelAttributeGroup(attrGroupRef.ref_);
      const attribute_defs &grdefs = registry_.attributeGroups_.get(attrGroupRef.ref_);
      attrdefs.attributes_.insert(attrdefs.attributes_.end(), 
				  grdefs.attributes_.begin(), grdefs.attributes_.end());
    }
    else {
      assert(!"Must not reach here.");
    }
  }
  // Take care of namespace??? !!!!!!
  attrdefs.has_anyAttribute_ = (decls.anyAttribute_.get() != 0); 
}

void processor::process_attribute(const aka::qname &hint,
				  attribute_defs &attrdefs, const xs::attribute &attr) {
  
  attribute_type attrtype;
  if (!attr.name_.empty())
    attrtype.name_ = create_attribute_name(attr.name_, attr.form_);
  else
    attrtype.name_ = create_attribute_name(attr.ref_, attr.form_);
  
  /** Get type */
  if (!attr.ref_.empty()) {
    if (!attr.type_.empty() || (attr.simpleType_.get() != 0))
      throw fatal_error();
    attrtype.type_ = registry_.attributes_.resolve(attr.ref_);
  }
  else if (!attr.type_.empty()) {
    if (attr.simpleType_.get() != 0)
      throw fatal_error();
    attrtype.type_ = attr.type_;
  }
  else if (attr.simpleType_.get() != 0) {
    aka::qname name(hint.qualified() + "_" + attr.name_);
    attrtype.type_ = process_localSimpleType(name, *attr.simpleType_.get());
  }
  else {
    // ur-type.
    attrtype.type_ = aka::qname("xs:anySimpleType");
  }

  if (!attr.default_.empty())
    attrtype.default_ = attr.default_;
  else if (!attr.fixed_.empty()) {
    attrtype.default_ = attr.fixed_;
    attrtype.is_fixed_ = true;
  }
  attrdefs.attributes_.push_back(attrtype);
}

void processor::restrict_attrDecls(attribute_defs &attrdefs, const xs::attrDecls &attrDecls) {

  for (xs::attributeChoice::const_iterator ait = attrDecls.choice_.begin();
       ait != attrDecls.choice_.end(); ++ait) {
    if (ait->get_name() == aka::qname("xs:attribute")) {
      const xs::attribute &attr = aka::item_cast<xs::attribute>(*ait);
      restrict_attribute(attrdefs, attr);
    }
    else if (ait->get_name() == aka::qname("xs:attributeGroup")) {
      assert(!"Not implemented.");
    }
    else {
      assert(!"Must not reach here.");
    }
  }

  // anyAttribute to process? !!!!!!!!
}

void processor::restrict_attribute(attribute_defs &attrdefs, const xs::attribute &attr) {
  attribute_type &atype = attrdefs.get(attr.name_);
  if (attr.use_ == "required")
    atype.required_ = true;
  else if (attr.use_ == "prohibit")
    attrdefs.erase(attr.name_);
  else if (attr.use_ == "optional")
    atype.required_ = false;

  if (!attr.default_.empty()) {
    atype.default_ = attr.default_;
    atype.is_fixed_ = false;
  }
  else if (!attr.fixed_.empty()) {
    atype.default_ = attr.fixed_;
    atype.is_fixed_ = true;
  }

  // restriction any_attribute.... !!!!!!!
}

void processor::extend_attrDecls(const aka::qname &name,
				 attribute_defs &attrdefs, const xs::attrDecls &attrDecls) {

  for (xs::attributeChoice::const_iterator ait = attrDecls.choice_.begin();
       ait != attrDecls.choice_.end(); ++ait) {
    if (ait->get_name() == aka::qname("xs:attribute")) {
      const xs::attribute &attr = aka::item_cast<xs::attribute>(*ait);
      extend_attribute(name, attrdefs, attr);
    }
    else if (ait->get_name() == aka::qname("xs:attributeGroup")) {
      const xs::attributeGroupRef &agref = aka::item_cast<xs::attributeGroupRef>(*ait);
      const xs::namedAttributeGroup &ag = topLevelAttributeGroups_.get(agref.ref_);
      extend_attrDecls(name, attrdefs, ag.attrDecls_);
    }
    else {
      assert(!"Must not reach here.");
    }
  }
}


void processor::extend_attribute(const aka::qname &name,
				 attribute_defs &attrdefs, const xs::attribute &attr) {

  if (!attrdefs.exists(attr.name_)) {
    process_attribute(name, attrdefs, attr);
  }
  else {
    //attribute_type &atype = attrdefs.get(attr.name_);
    // extension specification to be filled. !!!!!!!
  }
}


const class_def &processor::process_namedGroup(const aka::qname &name, const xs::namedGroup *gr) {
  if (registry_.type_exists(name))
    return *registry_.classes_.get(name);

  if (gr == 0)
    gr = &namedGroups_.get(name);

  assert(gr->particle_.size() == 1);
  const aka::item &item = gr->particle_.front();
  if (item.get_name() == aka::qname("xs:sequence"))
    return process_sequence(name, aka::item_cast<xs::explicitGroup>(item));
  else if (item.get_name() == aka::qname("xs:choice"))
    return process_choice(name, aka::item_cast<xs::explicitGroup>(item));
  else if (item.get_name() == aka::qname("xs:all"))
    return process_all(name, aka::item_cast<xs::all>(item));

  assert(!"Must not reach here.");
  return *static_cast<class_def*>(0);
}

aka::qname processor::process_typeChoice(const aka::qname &name, const xschoice &choice) {
  if (choice.size() != 1)
    throw fatal_error();

  const aka::item &item = *choice.begin();
  if (item.get_name() == aka::qname("xs:simpleType"))
    return process_localSimpleType(name, aka::item_cast<const xs::localSimpleType>(item));
  else if (item.get_name() == aka::qname("xs:complexType"))
    return process_localComplexType(name, aka::item_cast<const xs::localComplexType>(item));
  assert(!"Must not reach here.");
  return aka::qname();
}

aka::qname processor::process_localSimpleType(const aka::qname &name, 
					      const xs::localSimpleType &lst) {
  aka::qname type = process_simpleDerivation(lst.simpleDerivation_); 
  class_def *def = new simpletype_def(name);
  def->value_type_ = type;
  registry_.classes_.insert(name, def);
  registry_.simpleTypes_.add(name); /////!!!!!!!!
  return name;
}

aka::qname processor::process_localComplexType(const aka::qname &name,
					       const xs::localComplexType &lct) {
  process_complexTypeModel(name, lct.model_);
  return name;
}


const class_def &processor::process_complexTypeModel(const aka::qname &name, 
						     const xs::complexTypeModel &model) {
  
  if (model.size() == 0) { // <xs:complexType name="hoge"/>
    class_def *def = new sequence_def(name, aka::occurrence());
    registry_.classes_.insert(name, def);
    return *def;
  }

  const class_def *def;

  assert(model.size() == 1);
  const aka::item &item = model.front();
  if (item.get_name() == aka::qname("xs:simpleContent")) {
    def = &process_simpleContent(name, aka::item_cast<xs::simpleContent>(item));
  }
  else if (item.get_name() == aka::qname("xs:complexContent")) {
    def = &process_complexContent(name, aka::item_cast<xs::complexContent>(item));
  }
  else if (item.get_name() == aka::qname("&contentDef")) {
    const xs::contentDef &cdef = aka::item_cast<xs::contentDef>(item);
    class_def * seqdef = new sequence_def(name, aka::occurrence());
    element_type etype = process_complexTypeParticle(name, cdef.particle_);
    if (!etype.empty())
      seqdef->etypes_.push_back(etype);
    process_attrDecls(name, seqdef->adefs_, cdef.attrDecls_);
    registry_.classes_.insert(name, seqdef);
    def = seqdef;
  }

  assert(def != 0);
  def->adefs_.check_dup_names();
  return *def;
}

const class_def &processor::process_simpleContent(const aka::qname &name,
						  const xs::simpleContent &sc) {
  const xs::simpleContentChoice &choice = sc.value_type_;
  assert(choice.size() == 1);

  const aka::item &item = choice.front();
  if (item.get_name() == aka::qname("xs:extension"))
    return process_simpleExtensionType(name, aka::item_cast<xs::simpleExtensionType>(item));
  else if (item.get_name() == aka::qname("xs:restriction"))
    return process_simpleRestrictionType(name, aka::item_cast<xs::simpleRestrictionType>(item));
  
  assert(!"Must not reach here.");
  return *static_cast<const class_def*>(0);
}


const class_def &processor::process_simpleExtensionType(const aka::qname &name, 
							const xs::simpleExtensionType &se) {
  if (registry_.classes_.exists(se.base_)) {
    const class_def *super_def = registry_.classes_.get(se.base_);
    if (super_def->id_ != simplecontent_id)
      throw fatal_error();
    class_def *def = super_def->derive(name);
    process_attrDecls(name, def->adefs_, se.attrDecls_);
    registry_.classes_.insert(name, def);
    return *def;
  }
  else if (registry_.simpleTypes_.exists(se.base_)) {
    simplecontent_def *def = new simplecontent_def(name);
    def->value_type_ = se.base_;
    process_attrDecls(name, def->adefs_, se.attrDecls_);
    registry_.classes_.insert(name, def);
    return *def;
  }

  throw fatal_error();
  return *static_cast<const class_def*>(0);
}

const class_def &processor::process_simpleRestrictionType(const aka::qname &name, 
							  const xs::simpleRestrictionType &sr) {
  if (registry_.classes_.exists(sr.base_)) {
    const class_def *super_def = registry_.classes_.get(sr.base_);
    if (super_def->id_ != simpletype_id)
      throw fatal_error();
    class_def *def = super_def->derive(name);
    //process_attrDecls(*def->adefs_, se.attrDecls_);  !!!!!!!!!!!!!!!!
    // add code for prohibit !!!!!!!!!!!!!!!!!
    registry_.classes_.insert(name, def);
    return *def;
  }
  else if (registry_.simpleTypes_.exists(sr.base_)) {
    class_def *def = new simplecontent_def(name);
    def->value_type_ = sr.base_;
    registry_.classes_.insert(name, def);
    return *def;
  }

  throw fatal_error();
  return *static_cast<const class_def*>(0);
}


element_type processor::process_complexTypeParticle(const aka::qname &name,
						    const xs::typeDefParticle &particle) {
  
  if (particle.empty()) {
    return element_type();
  }

  assert(particle.size() == 1); // Must contain one paticle.
  const aka::item &item = particle.front();
  if (item.get_name() == aka::qname("xs:group")) {
    const xs::groupRef &gr = aka::item_cast<xs::groupRef>(item);
    process_namedGroup(gr.ref_); // Make sure referred group processed.
    element_type etype;
    etype.name_ = gr.ref_.local();
    etype.type_ = gr.ref_;
    etype.occ_ = occurrence(gr);
    etype.is_group_ = true;
    return etype;
  }

  element_type el;
  const class_def *particle_def;

  if (item.get_name() == aka::qname("xs:all")) {
    const xs::all &all = aka::item_cast<xs::all>(item);
    aka::qname allname = name;
    allname.local() += "_all";
    particle_def = &process_all(allname, all);
    el.occ_ = occurrence(all);
    el.name_ = aka::qname("a*");
  }
  else if (item.get_name() == aka::qname("xs:choice")) {
    const xs::explicitGroup &gr = aka::item_cast<xs::explicitGroup>(item);
    aka::qname choicename(name.qualified() + "_choice");
    particle_def = &process_choice(choicename, gr);
    el.occ_ = occurrence(gr);
    el.name_ = aka::qname("c*");
  }
  else if (item.get_name() == aka::qname("xs:sequence")) {
    const xs::explicitGroup &gr = aka::item_cast<const xs::explicitGroup>(item);
    aka::qname seqname = name;
    seqname.local() += "_sequence";
    particle_def = &process_sequence(seqname, gr);
    el.occ_ = occurrence(gr);
    el.name_ = aka::qname("s*");
  }
  else {
    throw fatal_error();
  }
  
  el.type_ = particle_def->name_;
  el.is_group_ = true;
  return el;
}


const class_def &processor::process_sequence(const aka::qname &name, 
					     const xs::explicitGroup &gr) {

  class_def *def = new sequence_def(name, occurrence(gr));
  registry_.classes_.insert(name, def);
  process_sequence_members(def->etypes_, name, gr);
  return *def;
}

const class_def &processor::process_choice(const aka::qname &name, 
					   const xs::explicitGroup &gr) {
  choice_def *def = new choice_def(name, aka::occurrence(gr.minOccurs_, gr.maxOccurs_));
  registry_.classes_.insert(name, def);
  process_choice_items(def->etypes_, name, gr);
  return *def;
}

const class_def &processor::process_all(const aka::qname &name, const xs::all &all) {
  all_def *def = new all_def(name, occurrence(all));
  registry_.classes_.insert(name, def);
  process_all_members(def->etypes_, name, all);
  return *def;
}

void processor::process_groupRef(const aka::qname &name, const xs::groupRef &gr) {
  const class_def *refdef = registry_.classes_.get(gr.ref_);
  class_def *def = refdef->derive(name);
  registry_.classes_.insert(name, def);
}

aka::qname processor::process_simpleDerivation(const xs::simpleDerivation &der) {
  assert(der.size() == 1);
  const aka::item &item = der.front();
  if (item.get_name() == aka::qname("xs:restriction")) {
    const xs::restriction &re = aka::item_cast<xs::restriction>(item);
    process_topLevelSimpleType(re.base_);
    return re.base_;
  }
  return aka::qname("xs:anySimpleType");
}

void processor::process_all_members(element_types &etypes, 
				    const aka::qname &hint, const xs::all &all) {

  const xs::allModelElements &elements = all.allModel_.elements_;
  for (xs::allModelElements::const_iterator it = elements.begin();
       it != elements.end(); ++it) {
    element_type membertype = process_allElementMember(*it, hint);
    etypes.push_back(membertype);
  }
  etypes.check_dup_names();
}

void processor::process_choice_items(element_types &etypes, 
				     const aka::qname &hint, const xs::explicitGroup &gr) {
  for (xschoice::const_iterator it = gr.particle_.begin(); 
       it != gr.particle_.end(); ++it) {
    element_type itemtype = process_particle(*it, hint);
    etypes.push_back(itemtype);
  }
  etypes.check_dup_names();
}

void processor::process_sequence_members(element_types &etypes, const aka::qname &hint,
					 const xs::explicitGroup &seq) {
  const xschoice &particles = seq.particle_; // particle.
  
  for (xschoice::const_iterator it = particles.begin();
       it != particles.end(); ++it) {
    element_type el = process_particle(*it, hint);
    etypes.push_back(el);
  }
  etypes.check_dup_names();
}


element_type processor::process_localElement(const xs::localElement &el, const aka::qname &hint) {
  // block, form, nillable is ignored.

  aka::qname type;
  aka::qname name;

  if (!el.ref_.empty()) {
    if (!el.type_.empty() || !(el.typeChoice_.empty()))
      throw fatal_error();
    type = process_topLevelElement(el.ref_);
    name = el.ref_;
  }
  else if (!el.type_.empty()) {
    if (!el.typeChoice_.empty())
      throw fatal_error();
    name = aka::qname(target_ns_id_, el.name_);
    type = el.type_;
  }
  else if (!el.typeChoice_.empty()) {
    name = aka::qname(target_ns_id_, el.name_);
    type = aka::qname(target_ns_id_, hint.local() + '_' + name.local());
    type = process_typeChoice(type, el.typeChoice_);
  }
  else {
    /* @ref, @type is not spcified.  content is ur-type. */
    name = aka::qname(target_ns_id_, el.name_);
    type = aka::qname("xs:anyType");
  }

  assert(!name.empty());
  assert(!type.empty());

  element_type et;

  et.name_ = create_element_name(name, el.form_);
  et.type_ = type;
  et.occ_ = aka::occurrence(el.minOccurs_, el.maxOccurs_);

  if (!el.fixed_.empty()) {
    if (!el.default_.empty())
      throw fatal_error();
    et.default_ = el.fixed_;
    et.is_fixed_ = true;
  }
  else if (!el.default_.empty()) {
    et.default_ = el.default_;
  }


  
  return et;
}


element_type processor::process_allElementMember(const xs::allModelElement &ame, 
						 const aka::qname &hint) {
  xs::localElement lel;

  lel.attrs_ = ame.attrs_;
  lel.id_ = ame.id_;
  lel.annotation_ = ame.annotation_;
  lel.ref_ = ame.ref_;
  lel.name_ = ame.name_;
  lel.typeChoice_ = ame.typeChoice_;
  lel.identityConstraint_ = ame.identityConstraint_;
  lel.type_ = ame.type_;
  lel.default_ = ame.default_;
  lel.fixed_ = ame.fixed_;
  lel.nillable_ = ame.nillable_;
  lel.block_ = ame.block_;
  lel.form_ = ame.form_;

  return process_localElement(lel, hint);
}


element_type processor::process_member_groupRef(const xs::groupRef &ref) {
  element_type et;
  et.name_ = ref.ref_.local();
  et.type_ = ref.ref_;
  et.occ_ = aka::occurrence(ref.minOccurs_, ref.maxOccurs_);
  et.is_group_ = true;
  return et;
}

element_type processor::process_any(const xs::any &any) {
  element_type et;
  et.occ_ = aka::occurrence(any.minOccurs_, any.maxOccurs_);
  et.name_ = aka::qname("any");
  et.type_ = aka::qname("xs:anyType");
  et.is_group_ = true;
  return et;
}

const class_def &processor::process_complexContent(const aka::qname &name, 
						   const xs::complexContent &cc) {
  const xs::complexContentContents &contents = cc.contents_;
  assert(contents.size() == 1);
  const aka::item &item = contents.front();
  if (item.get_name() == aka::qname("xs:restriction"))
    return process_complexRestrictionType(name, 
					  aka::item_cast<xs::complexRestrictionType>(item));
  else if (item.get_name() == aka::qname("xs:extension"))
    return process_extensionType(name, aka::item_cast<xs::extensionType>(item));

  assert(!"Must not reach here.");
  return *static_cast<const class_def*>(0);
}

const class_def &processor::process_complexRestrictionType(const aka::qname &name, 
							   const xs::complexRestrictionType &cr) {
  const class_def &superdef = process_topLevelComplexType(cr.base_);
  // lack of !Restriction check!.!!!!!
  class_def *def = new sequence_def(name, aka::occurrence());
  element_type etype = process_complexTypeParticle(name, cr.typeDefParticle_);
  if (!etype.empty())
    def->etypes_.push_back(etype);
  def->adefs_ = superdef.adefs_;
  
  registry_.classes_.insert(name, def);

  restrict_attrDecls(def->adefs_, cr.attrDecls_);
  return *def;
}

const class_def &processor::process_extensionType(const aka::qname &name, 
						  const xs::extensionType &et) {

  const class_def *superdef = &process_topLevelComplexType(et.base_);
  class_def *def = superdef->derive(name);

  if (def->etypes_.empty()) {
    element_type etype = process_complexTypeParticle(name, et.typeDefParticle_);
    if (!etype.empty())
      def->etypes_.push_back(etype);
  }
  else {
    element_type el;
    el = process_complexTypeParticle(name, et.typeDefParticle_);
    if (!el.empty()) {
      aka::qname particlename(name.qualified() + "_particle");
      class_def *particle_def = new sequence_def(particlename, aka::occurrence());
      particle_def->etypes_ = def->etypes_;
      registry_.classes_.insert(particlename, particle_def);
      def->etypes_.clear();
//       el.name_ = aka::qname("@");
      particle_def->etypes_.push_back(el);

      el.name_ = name.local(); // extended member name.
      el.type_ = particlename;
      el.is_group_ = true;
      def->etypes_.push_back(el);
    }
  }
  extend_attrDecls(name, def->adefs_, et.attrDecls_);
  registry_.classes_.insert(name, def);
  return *def;

  // !!!! lack of @block check.

}

// void processor::process_child_types(element_types &etypes,
// 				    const aka::qname &hint,
// 				    const xs::typeDefParticle &particle) {

//   if (particle.empty()) {
//     // <xs:complexType/> nothing to do.
//   }
//   else {
//     assert(particle.size() == 1);
//     const aka::item &item = particle.front();
//     if (item.get_name() == aka::qname("xs:group")) {
//       const xs::groupRef &gr = aka::item_cast<xs::groupRef>(item);
//       element_type et = process_member_groupRef(gr);
//       etypes.push_back(et);
//     }
//     else if (item.get_name() == aka::qname("xs:all")) {
//       const xs::all &all = aka::item_cast<xs::all>(item);
//       process_all_members(etypes, hint, all);
//     }
//     else if (item.get_name() == aka::qname("xs:choice")) {
//       const xs::explicitGroup &gr = aka::item_cast<xs::explicitGroup>(item);
//       process_choice_items(etypes, hint, gr);
//     }
//     else if (item.get_name() == aka::qname("xs:sequence")) {
//       const xs::explicitGroup &gr = aka::item_cast<xs::explicitGroup>(item);
//       process_sequence_members(etypes, hint, gr);
//     }
//     else {
//       assert(!"Must not reach here.");
//     }
//   }
// }



element_type processor::process_particle(const aka::item &item, const aka::qname &hint) {
  
  size_t all_index = 0;
  size_t choice_index = 0;
  size_t sequence_index = 0;
  element_type el;


  if (item.get_name() == aka::qname("xs:element")) {
    el = process_localElement(aka::item_cast<const xs::localElement>(item), hint);
  }
  else if (item.get_name() == aka::qname("xs:group")) {
    el = process_member_groupRef(aka::item_cast<const xs::groupRef>(item));
  }
  else if (item.get_name() == aka::qname("xs:all")) {
    aka::qname allname = create_anonymous_name(hint, "a", all_index);
    el = process_localAll(allname, aka::item_cast<const xs::all>(item));
    el.name_ = aka::qname("a*");
    ++all_index;
  }
  else if (item.get_name() == aka::qname("xs:choice")) {
    aka::qname choicename = create_anonymous_name(hint, "c", choice_index);
    el = process_localChoice(choicename, aka::item_cast<const xs::explicitGroup>(item));
    el.name_ = aka::qname("c*");
    ++choice_index;
  }
  else if (item.get_name() == aka::qname("xs:sequence")) {
    aka::qname sequencename = create_anonymous_name(hint, "s", sequence_index);
    el = process_localSequence(sequencename, aka::item_cast<const xs::explicitGroup>(item));
    el.name_ = aka::qname("s*");
    ++sequence_index;
  }
  else if (item.get_name() == aka::qname("xs:any")) {
    el = process_any(aka::item_cast<const xs::any>(item));
    el.is_group_ = true;
  }
  else {
    assert(!"Must not reach here.");
  }
  return el;
}


element_type processor::process_localAll(const aka::qname &name, const xs::all &all) {
  process_all(name, all);
  element_type et;
  et.name_ = name.local();
  et.occ_ = aka::occurrence(all.minOccurs_, all.maxOccurs_);
  et.type_ = name;
  et.is_group_ = true;
  return et;
}

element_type processor::process_localChoice(const aka::qname &name, const xs::explicitGroup &cho) {
  process_choice(name, cho);
  element_type et;
  et.name_ = name.local();
  et.occ_ = aka::occurrence(cho.minOccurs_, cho.maxOccurs_);
  et.type_ = name;
  et.is_group_ = true;
  return et;
}

element_type processor::process_localSequence(const aka::qname &name, 
					      const xs::explicitGroup &seq) {
  process_sequence(name, seq);
  element_type et;
  et.name_ = name.local();
  et.occ_ = aka::occurrence(seq.minOccurs_, seq.maxOccurs_);
  et.type_ = name;
  et.is_group_ = true;
  return et;
}
