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

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

#include <akaxiso/classes/simpletype.h>
#include <akaxiso/classes/container_methods.h>
#include <akaxiso/classes/equals.h>

namespace aka2 {

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

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

    virtual bool has_next() const { return current_ != container_.end(); }
    virtual const void *next() {
      assert(has_next());
      const TYPENAME C::value_type &v = *current_;
      ++current_;
      return &v;
    }
  };

  template<class L, class VL>
  class array_op_dispatcher : public array_op {
  public:
    virtual schematype_id get_schematype() const {  return array_id;  }
    virtual const qname& get_typename() const {  return L::typename_; }
    virtual const attribute_types *get_attribute_types() const { return 0; }
    virtual const any_member *get_anyattr_type() const { return 0; }

    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(); }

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

    virtual size_t size(const void *container) const { return L::size(container); }
    virtual void push(void *container, void *item) const {
      L::push_method(container, item);
    }
    virtual const element_op& get_item_op() const {
      return VL::dispatcher_;
    }
    virtual array_iterator* get_iterator(const void *e) const {
      return L::get_iterator(e);
    }
  };

  template<class VL, class L>
  struct array_statics {
    static qname typename_;
    static occurrence occ_;
    static array_op_dispatcher<L, VL> dispatcher_;
  };

  template<class VL, class L>
  qname array_statics<VL, L>::typename_;

  template<class VL, class L>
  aka2::occurrence array_statics<VL, L>::occ_;

  template<class VL, class L>
  array_op_dispatcher<L, VL> array_statics<VL, L>::dispatcher_;


  template<class T, class L, class VL>
  class array : public array_statics<VL, L> {
  public:
    virtual ~array(){}
    typedef L leaf_type;
    typedef T value_type;
    typedef TYPENAME T::value_type item_type;
    typedef VL item_leaf_type;

    static void initialize() {
      if (!system_type_registry().add(L()))
	return;
      VL::initialize();
    }
    static void uninitialize() {}

    // To define model.
    static void push_method(void *container, const void *item) {
      L().methods.push(*static_cast<T*>(container),
		       *static_cast<const item_type*>(item));
    }

    static void construct(void *e) {
      new (e) T();
    }
    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 array_equals(lhs, rhs, L::dispatcher_, VL::dispatcher_);
    }

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

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

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

    static default_op *create_default_op() { return 0; }
  };




  template<class T, class VL=xiso::leaf<TYPENAME T::value_type> >
  class sequential_array : public array<T, sequential_array<T, VL> , VL> {
  public:
    sequential<T> methods;
  };


  template<class T, class VL=xiso::leaf<TYPENAME T::value_type> >
  class associative_array : public array<T, associative_array<T, VL>, VL> {
  public:
    associative<T> methods;
  };

} // namespace aka2

#endif /* AKAXISO_ARRAY_H__ */
