/* -*- c++ -*- */
#ifndef PROCESSOR_H__
#define PROCESSOR_H__

#include <akaxiso/schema/schema.h>
#include "registry.h"
#include "exception.h"

typedef std::list<aka::item> xschoice;

template<class T>
class declarations {
  struct value {
    value() : t_(0){}
    value(const T*t, const aka::qname &name) : name_(name), t_(t){}
    aka::qname name_;
    const T* t_;
  };
  typedef std::map<aka::qname, value, aka::qname_less> cache;
  cache cache_;
public:
  void add(const aka::qname &name, const T* t);
  const T& get(const aka::qname &name);
  typedef TYPENAME cache::iterator iterator;

  iterator begin() { return cache_.begin(); }
  iterator end() { return cache_.end(); }
};

template<class T>
void declarations<T>::add(const aka::qname &name, const T* t) {
  std::pair<TYPENAME cache::iterator, bool> res =
    cache_.insert(TYPENAME cache::value_type(name, value(t, name)));
  if (!res.second)
    throw fatal_error();
}

template<class T>
const T& declarations<T>::get(const aka::qname &name) {
  TYPENAME cache::iterator it = cache_.find(name);
  if (it == cache_.end())
    throw type_not_found(name, __FILE__, __LINE__);
  return *it->second.t_;
}




template<class T>
bool is_array(const T& t) {
  return (t.minOccurs_ != 1) || (t.maxOccurs_ != 1);
}

template<class T>
aka::occurrence occurrence(const T& t) {
  return aka::occurrence(t.minOccurs_, t.maxOccurs_);
}


class processor {
public:
  processor(registry &reg, const std::string &basepath, std::ostream &ostm, bool verbose)
    : registry_(reg), basepath_(basepath), ostm_(ostm), verbose_(verbose) {}
  void preprocess(const xs::schema &schema);
  void process(const xs::schema &schema);
  void generate();

  void write_element_header(const std::string &filename, std::ostream &ostm);
  void write_xiso_header(const std::string &element_fn, 
			 const std::string &xiso_fn, 
			 std::ostream &ostm);
  void write_xiso_implementation(const std::string &xiso_header, 
				 const std::string &xiso_impl,
				 std::ostream &ostm);
  
  aka::id_type get_target_ns_id() const { return target_ns_id_; }

private:

  // preprocess
  void process_include(const xs::_include &include);
  void process_import(const xs::import &import);
  void process_redefine(const xs::redefine &redefine);

  // Declaration of explicitly-named types.
  void scan_topLevelTypes(const xs::schema &schema);

  // For schemaTop.
  aka::qname process_topLevelElement(const aka::qname &name, const xs::topLevelElement *el = 0);
  void check_reference(const xs::topLevelElement &el);

  void process_topLevelAttribute(const aka::qname &name, 
				 const xs::topLevelAttribute &attr);
  void process_topLevelAttributeGroup(const aka::qname &name, 
				      const xs::namedAttributeGroup *ag = 0);
  // For redefinables.
  void process_redefinable(const xs::redefinable &redefinable);
  void process_topLevelSimpleType(const aka::qname &name, 
				  const xs::topLevelSimpleType *st = 0);
  const class_def &process_topLevelComplexType(const aka::qname &name, 
					       const xs::topLevelComplexType *ct = 0);
  const class_def &process_namedGroup(const aka::qname &name, 
				      const xs::namedGroup *gp = 0);

  void process_attrDecls(const aka::qname &hint, 
			 attribute_defs &attrdefs, const xs::attrDecls &decls);
  static bool is_attrDeclsEmpty(const xs::attrDecls &decls);

  void process_attribute(const aka::qname &hint,
			 attribute_defs &attrdefs, const xs::attribute &attr);
  aka::qname process_localSimpleType(const aka::qname &hint, const xs::localSimpleType &lst);
  aka::qname process_localComplexType(const aka::qname &name, const xs::localComplexType &lct);
  aka::qname process_typeChoice(const aka::qname &hint, const xschoice &choice);
  
  const class_def &process_complexTypeModel(const aka::qname &name, 
					    const xs::complexTypeModel &model);
  element_type process_complexTypeParticle(const aka::qname &name,
					   const xs::typeDefParticle &particle);
  const class_def &process_restrictionClassDeclaration(const aka::qname &name,
						       const class_def &super_def,
						       const xs::typeDefParticle &particle, 
						       const xs::attrDecls &attrDecls);
//   void process_child_types(element_types &etypes,
// 			   const aka::qname &hint,
// 			   const xs::typeDefParticle &particle);

  const class_def &process_complexContent(const aka::qname &name, const xs::complexContent &cc);
  const class_def &process_complexRestrictionType(const aka::qname &name, 
						  const xs::complexRestrictionType &cr);
  const class_def &process_extensionType(const aka::qname &name, 
					 const xs::extensionType &et);

  const class_def &process_simpleContent(const aka::qname &name, const xs::simpleContent &sc);
  const class_def &process_simpleRestrictionType(const aka::qname &name, 
						 const xs::simpleRestrictionType &sr);
  const class_def &process_simpleExtensionType(const aka::qname &name, 
					       const xs::simpleExtensionType &se);

  const class_def &process_sequence(const aka::qname &name, const xs::explicitGroup &seq);
  const class_def &process_choice(const aka::qname &name, const xs::explicitGroup &cho);
  const class_def &process_all(const aka::qname &name, const xs::all &all);


  void process_sequence_members(element_types &etypes, const aka::qname &hint,
				const xs::explicitGroup &seq);
  void process_choice_items(element_types &etypes, 
			    const aka::qname &hint, const xs::explicitGroup &cho);
  void process_all_members(element_types &etypes, const aka::qname &hint, const xs::all &all);

  void process_groupRef(const aka::qname &name, const xs::groupRef &gr);

  aka::qname process_simpleDerivation(const xs::simpleDerivation &der);

  element_type process_particle(const aka::item &item, const aka::qname &hint);
  element_type process_localElement(const xs::localElement &le, const aka::qname &hint);

  element_type process_localSequence(const aka::qname &name, const xs::explicitGroup &seq);
  element_type process_localChoice(const aka::qname &name, const xs::explicitGroup &cho);
  element_type process_localAll(const aka::qname &name, const xs::all &all);

  element_type process_allElementMember(const xs::allModelElement &ame, const aka::qname &hint);
  element_type process_any(const xs::any &any);
  element_type process_member_groupRef(const xs::groupRef &ref);

  void restrict_attrDecls(attribute_defs &attrdefs, const xs::attrDecls &attrDecls);
  void restrict_attribute(attribute_defs &attrdefs, const xs::attribute &attr);
  void extend_attrDecls(const aka::qname &name, 
			attribute_defs &attrdefs, const xs::attrDecls &attrDecls);
  void extend_attribute(const aka::qname &name,
			attribute_defs &attrdefs, const xs::attribute &attr);

  
  static aka::qname create_anonymous_name(const aka::qname &name, const std::string &postfix,
					  size_t index);
  aka::qname create_attribute_name(const aka::qname &name, const std::string &form);
  aka::qname create_element_name(const aka::qname &name, const std::string &form);
  aka::qname create_array_name(const aka::qname &name);
  static bool is_form_qualified(const std::string &form);

  registry &registry_;
  aka::id_type target_ns_id_;
  bool element_qualified_;
  bool attribute_qualified_;

  typedef declarations<xs::topLevelComplexType> topLevelComplexTypes;
  typedef declarations<xs::topLevelElement> topLevelElements;
  typedef declarations<xs::topLevelAttribute> topLevelAttributes;
  typedef declarations<xs::namedAttributeGroup> topLevelAttributeGroups;
  typedef declarations<xs::topLevelSimpleType> topLevelSimpleTypes;
  typedef declarations<xs::namedGroup> namedGroups;

  topLevelComplexTypes topLevelComplexTypes_;
  topLevelElements topLevelElements_;
  topLevelAttributes topLevelAttributes_;
  topLevelAttributeGroups topLevelAttributeGroups_;
  topLevelSimpleTypes topLevelSimpleTypes_;
  namedGroups namedGroups_;

  qname_set attrG_processing_;
  qname_set complexType_processing_;

  std::string basepath_;
  std::ostream &ostm_;
  bool verbose_;
};

#endif
