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

/** @file akaxiso/classes/sequence.h */

#include <akaxiso/classes/simpletype.h>
#include <akaxiso/classes/attribute.h>
#include <akaxiso/classes/memberdef.h>
#include <akaxiso/classes/closure.h>
#include <akaxiso/classes/model_check.h>

namespace aka2 {

  template<class L>
  class sequence_op_dispatcher : public sequence_op {
  public:
    virtual schematype_id get_schematype() const { return sequence_id; }
    virtual const qname& get_typename() const { return L::typename_; }
    /** creatable */
    virtual bool equals(const void *lhs, const void *rhs) const {
      return L::equals(lhs, rhs);
    }

    virtual void construct(void *e) const {  L::construct(e); }
    virtual void copy_construct(void *e, const void *src) const { L::copy_construct(e, src); }
    virtual void destruct(void *e) const { L::destruct(e); }
    virtual size_t class_size() const { return L::class_size(); }

    /** attribute_info getter. */
    virtual const attribute_types *get_attribute_types() const {
      return &L::attribute_types_;
    }
    /** sequence_info getter. */
    virtual const member_types &get_member_types() const {
      return L::member_types_;
    }
    virtual const any_member *get_anyattr_type() const { return L::get_anyattr_type(); }
  };


  template<class L, class T>
  struct sequence_statics {
    static member_types member_types_;
    static qname typename_;
    static sequence_op_dispatcher<L> dispatcher_;
    static occurrence occ_;
  };

  template<class L, class T>
  member_types sequence_statics<L, T>::member_types_;

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

  template<class L, class T>
  occurrence sequence_statics<L, T>::occ_;

  template<class L, class T>
  sequence_op_dispatcher<L> sequence_statics<L, T>::dispatcher_;


  template<class T, class L=xiso::leaf<T> >
  class sequence : public attributes<L, T>,
		   public sequence_statics<L, T>,
		   public memberdef<L, T> {
  public:
    typedef T value_type;

    virtual ~sequence(){}

    static member_type* register_membertype(const member_type &mtype) {
      L::member_types_.push_back(mtype);
      return &L::member_types_.back();
    }

    static void initialize() {
      if (!system_type_registry().add(L()))
      	return;
      L::member_types_.clear();
      L::attribute_types_.clear();
      L l; l.model();
      correct_member_occurrence(L::member_types_);
    }

    static void uninitialize() {
      L::member_types_.clear();
      L::attribute_types_.clear();
    }


    static void construct(void *e) {
      new (e) T();
      sequence_construct(e, L::dispatcher_);
    }
    static void copy_construct(void *e, const void *src) {
      new (e) T(*static_cast<const T*>(src));
    }
    static size_t class_size() { return sizeof(T); }
    static void destruct(void *elm) { static_cast<T*>(elm)->~T(); }


    static bool equals(const void *lhs, const void *rhs) {
      return sequence_equals(lhs, rhs, L::dispatcher_);
    }

    static element_op* get_attribute_dispatcher() { return &L::dispatcher_; }
    static default_op* create_default_op() { return 0; }

    static void occurrence(int minOccurs, int maxOccurs) {
      L::occ_ = aka2::occurrence(minOccurs, maxOccurs);
    } 


    static void xmltype(const std::string &name) {
      L::typeinfo_.set_name(qname(name));
    }

    static void enclose(const std::string &tagname) {
      member_type mtype =
      	member_type(new null_getter(), enclose_op::op_);
      mtype.set_name(tagname);
      L::register_membertype(mtype);
    }

    static void disclose(const std::string &tagname) {
      member_type mtype =
      	member_type(new null_getter(), disclose_op::op_);
      mtype.set_name(tagname);
      L::register_membertype(mtype);
    }

    template<class P>
    void any_ptrmember(const std::string &tagname, aka::deep_ptr<aka2::any> P::* m) {
      typedef aka2::ptrmember_op_stub<aka::deep_ptr<aka2::any>, aka2::any_op>
	op_type;
      member_getter *mgetter = 
	create_ptr_getter(reinterpret_cast<T*>(0), m);	  
      aka2::member_type 
	mtype(mgetter, op_type::dispatcher_);
      mtype.set_name(tagname);
      mtype.set_occurrence(aka2::occurrence(0, 1));
      L::register_membertype(mtype);
    }

    template<class P>
    void any(const std::string &tagname, aka2::any P::* m) {
      member_getter *mgetter = 
	create_ptr_getter(reinterpret_cast<T*>(0), m);	  
      aka2::member_type mtype(mgetter, any_op::dispatcher_);
      mtype.set_name(aka::qname(tagname));
      L::register_membertype(mtype);
    }

    template<class P>
    void any(const std::string &tagname, aka2::any_array P::* m, int minOccurs, int maxOccurs) {
      member_getter *mgetter = 
	create_ptr_getter(reinterpret_cast<T*>(0), m);	  
      aka2::member_type mtype(mgetter, any_array_op::dispatcher_);
      mtype.set_name(aka::qname(tagname));
      mtype.set_occurrence(aka2::occurrence(minOccurs, maxOccurs));
      L::register_membertype(mtype);
    }
  };

} // namespace aka2

#endif /* AKAXISO_SEQUENCE_H__ */
