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

#include <akaxiso/classes/qname.h>
#include <akaxiso/classes/item.h>

#include <vector>

namespace aka2 {


  template<class C>
  class typed_item_iterator : public item_iterator {
  public:
    typed_item_iterator(const C& container) : 
      container_(container), current_(container.begin()) {}
    virtual ~typed_item_iterator(){}

    const C &container_;
    TYPENAME C::const_iterator current_;

    virtual bool has_next() const { return current_ != container_.end(); }
    virtual const item *next() { 
      const item &v = *current_;
      ++current_;
      return &v;
    }
  };


  template<class L>
  class choice_op_dispatcher : public choice_op {
  public:
    virtual schematype_id get_schematype() const { return choice_id; }
    virtual const qname& get_typename() const { return L::typename_; }
    virtual const attribute_types* get_attribute_types() const { return 0; }
    /** creatable */
    virtual void* create() const { return L::create(); }
    virtual void destroy(void *elm) const { L::destroy(elm); }
    virtual bool equals(const void *lhs, const void *rhs) const {
      return L::equals(lhs, rhs);
    }
    virtual void copy(void *dest, const void *src) const {
      L::copy(dest, src);
    }

    virtual size_t size(const void *container) const { return L::size(container); }

    /** attribute_info getter. */
    virtual const itemtypes &get_itemtypes() const { return L::itemtypes_; }

    virtual void push(void *container, item &i) const {
      L::push_function(container, i);
    }
    virtual item_iterator* get_iterator(const void *e) const {
      return L::get_iterator(e);
    } 
  };


  template<class L, class T>
  struct choice_statics {
    static itemtypes itemtypes_;
    static qname typename_;
    static choice_op_dispatcher<L> dispatcher_;
  };

  template<class L, class T>
  itemtypes choice_statics<L, T>::itemtypes_;

  template<class L, class T>
  qname choice_statics<L, T>::typename_;

  template<class L, class T>
  choice_op_dispatcher<L> choice_statics<L, T>::dispatcher_;


  template<class T, class L>
  class choice : public choice_statics<L, T> {
  public:
    typedef T value_type;
    virtual ~choice(){}

    static void initialize() {
      if (!system_type_registory().add(L()))
	return;
      itemtypes_.clear();
      L l; l.model();
    }
    static void uninitialize() {
      itemtypes_.clear();
    }

    static typed_item_iterator<T>* get_iterator(const void *container) {
      return new typed_item_iterator<T>(*static_cast<const T*>(container));
    }

    static T* create() { 
      T * t = new T;
      // No construction required.
      return t; 
    }

    static void destroy(void *elm) { delete static_cast<T*>(elm); }
    static bool equals(const void *lhs, const void *rhs) {
      return choice_equals(lhs, rhs, dispatcher_);
    }
    static void copy(void *dest, const void *src) {
      choice_copy(dest, src, dispatcher_);
    }

    static size_t size(const void *container) { return static_cast<const T*>(container)->size(); }

    static void push_function(void *container, aka2::item &i) {
      L().methods.push(*static_cast<T*>(container), i);
    }

    struct item {
      template<class IL>
      item(const char *tagname, const IL&) {
	      IL::initialize();
	      qname name(tagname);
	      element_op *et = &IL::dispatcher_;
	      std::pair<itemtypes::iterator, bool> res = 
	      L::itemtypes_.insert(itemtypes::value_type(name, itemtype(et)));
	      if (!res.second)
	        throw internal_error();
	      itemtype_ = &res.first->second;
      }
      void occurence(int minOccurs, int maxOccurs) {
	      schematype_id id = itemtype_->get_schematype();
	      if ((id == array_id) || (id == choice_id)) 
	        itemtype_->set_occurence(aka2::occurence(minOccurs, maxOccurs));
	      else
	        throw internal_error();
      }
      itemtype *itemtype_;
    };

    static default_op* create_default_op() { return 0; }

    static const itemtypes::value_type* find_itemtype(const qname &name) {
      itemtypes::const_iterator it = itemtypes_.find(name);
      if (it == itemtypes_.end())
      	return 0;
      return &*it;
    }

    static const itemtypes::value_type* find_itemtype(const element_op &eo) {
      for (itemtypes::const_iterator it = itemtypes_.begin(); it != itemtypes_.end(); ++it)
    	if (it->second == &eo)
	      return &*it;
      return 0;
    }
  };


  template<class C, class L>
  struct sequential_choice_moc {
    typedef TYPENAME C::value_type value_type;
    sequential_choice_moc(C &container) : c_(container) {}
    TYPENAME C::iterator begin() { return c_.begin(); }
    TYPENAME C::iterator end() { return c_.end(); }
    TYPENAME C::const_iterator begin() const { return c_.begin(); }
    TYPENAME C::const_iterator end() const { return c_.end(); }
    C& c_;

    template<class I>
    void push_back(const I &v, const std::string &tagname) { 
      const itemtypes::value_type *vt = L::find_itemtype(tagname);
      if (vt == 0)
	      throw internal_error();
      c_.push_back(item(vt->first, new I(v), 
			vt->second.get_op())); 
    }

    template<class R> 
    int find_elements(R &r, const std::string &tagname) const {
      int count = 0;
      
      const itemtypes::value_type *itype = L::find_itemtype(tagname);
      if (itype == 0)
        throw internal_error();
      
      for (TYPENAME C::const_iterator it = c_.begin(); it != c_.end(); ++it) {
	if (it->get_name() == qname(tagname)) {
          r.push_back(item_cast<TYPENAME R::value_type>(*it));
	  ++count;
	}
      }
      return count;
    }
    
  };


  template<class T, class L=xiso::leaf<T> >
  class sequential_choice : public choice<T, L> {
  public:
    virtual ~sequential_choice(){}
    typedef  sequential_choice_moc<T, L> moc;
    typedef  sequential_choice_moc<const T, L> const_moc;
    sequential<T> methods;
  };


  template<class C, class L>
  struct associative_choice_moc {
    typedef TYPENAME C::value_type value_type;
    associative_choice_moc(C &container) : c_(container) {}
    TYPENAME C::iterator begin() { return c_.begin(); }
    TYPENAME C::iterator end() { return c_.end(); }
    TYPENAME C::const_iterator begin() const { return c_.begin(); }
    TYPENAME C::const_iterator end() const { return c_.end(); }
    C& c_;

    template<class I, class IL>
    void insert(const I &v) { 
      const element_op * op = global_type_registory().find(typeid(IL).name());
      if (op == 0)
        throw internal_error();
      const itemtypes::value_type *vt = L::find_itemtype(*op);
      if (vt == 0)
	      throw internal_error();
      c_.insert(item(vt->first, new I(v), vt->second.get_op())); 
    }

    template<class R> 
    int find_elements(R &r, const std::string &tagname) const {
      
      const itemtypes::value_type *itype = L::find_itemtype(tagname);
      if (itype == 0)
        throw internal_error();

      int count = 0;
      for (TYPENAME C::const_iterator it = c_.begin(); it != c_.end(); ++it) {
      	if (it->get_name() == qname(tagname)) {
          r.push_back(item_cast<TYPENAME R::value_type>(*it));
	        ++count;
    	  }
      }
      return count;
    }
    
  };


  template<class T, class L=xiso::leaf<T> >
  class associative_choice : public choice<T, L> {
  public:
    virtual ~associative_choice(){}

    typedef  associative_choice_moc<T, L> moc;
    typedef  associative_choice_moc<const T, L> const_moc;
    associative<T> methods;
  };




}




#endif
