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

#include <akaxiso/classes/fixed.h>
#include <akaxiso/classes/ptrmember.h>
#include <akaxiso/classes/any.h>
#include <akaxiso/classes/accessor.h>

namespace aka2 {

  template<class L, class T>
  struct memberdef {

    // member(tagname, member[, minOccurs, maxOccurs])
    // member(tagname, member, leaf[, minOccurs, maxOccurs])
    // fixed<leaf>(tagname, fixed_value)
    // fixed_array<leaf>(tagname, fixed_value, minOccurs, maxOccurs)
    // ptrmember(tagname, member)
    // ptrmember(tagname, member, leaf)

    // defined in sequence.h
    // any(member)
    // any(member, minOccurs, maxOccurs)

    static member_type *define_member(const std::string tagname, 
				      member_getter *mgetter,
				      element_op &op,
				      default_op *defop,
				      const occurrence &occ) {
      aka2::member_type mtype(mgetter, op);
      mtype.set_name(tagname); 
      if (defop != 0)
	mtype.set_default_op(defop);
      if (!check_occurrence(op.get_schematype(), occ))
	throw internal_error();
      mtype.set_occurrence(occ);
      return L::register_membertype(mtype);
    }
    
    // Helper class to define serializable member.
    struct _member {
      template<class P, class V> 
      _member(const std::string &tagname, V P::* m) {
      	new_member(tagname, m, xiso::leaf<V>(), 1, 1);
      }
      template<class P, class V> 
      _member(const std::string &tagname, V P::* m, int minOccurs, int maxOccurs) {
      	new_member(tagname, m, xiso::leaf<V>(), minOccurs, maxOccurs);
      }
      template<class P, class V, class VL> 
      _member(const std::string &tagname, V P::* m, const VL&) {
	new_member(tagname, m, VL(), 1, 1);
      }
      template<class P, class V, class VL> 
      _member(const std::string &tagname, V P::* m, const VL&, int minOccurs, int maxOccurs) {
	new_member(tagname, m, VL(), minOccurs, maxOccurs);
      }
      void set_default(const std::string &defval) {
	if (mtype_->get_schematype() != simpletype_id)
	  throw internal_error();
      	mtype_->setup_default_value(defval);
      };

    private:
      template<class P, class V, class VL> 
      void new_member(const std::string &tagname, V P::* m, const VL&, 
		      int minOccurs, int maxOccurs) {
      	VL::initialize();
	member_getter *mgetter = 
	  create_ptr_getter(reinterpret_cast<T*>(0), m);	  
	mtype_ = define_member(tagname, 
			       mgetter, 
			       VL::dispatcher_, 
			       VL::create_default_op(),
			       aka::occurrence(minOccurs, maxOccurs));
      }
      member_type *mtype_;
    };
    typedef _member member; // VC6 workaround.

    struct _ptrmember {
      template<class P, class V> 
      _ptrmember(const std::string &tagname, V P::* m) {
	new_ptr_member(tagname, m, xiso::leaf<TYPENAME V::value_type>());
      }
      template<class P, class V, class VL>
      _ptrmember(const std::string &tagname, V P::* m, const VL&) {
      	new_ptr_member(tagname, m, VL());
      }
    private:
      template<class P, class V, class VL> 
      void new_ptr_member(const std::string &tagname, V P::* m, const VL&) {
	VL::initialize();
	member_getter *mgetter = 
	  create_ptr_getter(reinterpret_cast<T*>(0), m);	  
	define_member(tagname, mgetter, 
		      aka2::ptrmember_op_stub<V, VL>::dispatcher_,
		      VL::create_default_op(),
		      occurrence(0, 1));
      }
    };
    typedef _ptrmember ptrmember; // VC6 workadound.

    /** Helper class to define serializable member. */
    template<class V>
    struct fixed_member { // Default value for complexType or mixed content will not be supported.
      fixed_member(const std::string &tagname, const std::string &fixed_value) {
      	new_member(tagname, xiso::leaf<V>(), fixed_value);
      }
      template<class VL>
      fixed_member(const std::string &tagname, const std::string &fixed_value, const VL &) {
      	new_member(tagname, VL(), fixed_value);
      }
    private:
      template<class VL>
      void new_member(const std::string &tagname, const VL&, const std::string &fixed_value) {
      	VL::initialize();
	if (VL::dispatcher_.get_schematype() != simpletype_id)
	  throw internal_error();
	member_type *mtype = define_member(tagname, new null_getter(),
					   aka2::fixed<VL>::dispatcher_,
					   VL::create_default_op(),
					   aka::occurrence());
	mtype->setup_default_value(fixed_value);
      }
    };
    
    // Helper class to define serializable member.
    struct _fixed_array { 
      // Default value for complexType or mixed content will not be supported.
      template<class P, class V>
      _fixed_array(const std::string &tagname, const std::string &fixed_value, V P::*m, 
		   int minOccurs, int maxOccurs) {
      	new_member(tagname, m, xiso::leaf<V>(),
		   minOccurs, maxOccurs, fixed_value);
      }
      template<class P, class V, class VL>
      _fixed_array(const std::string &tagname, const std::string &fixed_value, V P::*m, const VL &,
		   int minOccurs, int maxOccurs) {
      	new_member(tagname, m, VL(), minOccurs, maxOccurs, fixed_value);
      }
    private:
      template<class P, class V, class VL>
      void new_member(const std::string &tagname, V P::* m, const VL&, 
		      int minOccurs, int maxOccurs,
		      const std::string &fixed_value) {
      	VL::initialize();
	typedef TYPENAME VL::item_leaf_type item_leaf_type;

	if (VL::dispatcher_.get_schematype() != array_id)
	  throw internal_error();
	if (VL::dispatcher_.get_item_op().get_schematype() != fixed_id)
	  throw internal_error();
	const fixed_op &fop = static_cast<const fixed_op&>(VL::dispatcher_.get_item_op());
	if (fop.get_value_op().get_schematype() != simpletype_id)
	  throw internal_error();
	
	member_getter *mgetter = create_ptr_getter(static_cast<const T*>(0), m);
	member_type *mtype = define_member(tagname, mgetter,
					   VL::dispatcher_,
					   item_leaf_type::create_default_op(),
					   aka::occurrence(minOccurs, maxOccurs));
	mtype->setup_default_value(fixed_value);
      }
    };
    typedef _fixed_array fixed_array;
    
    /**
     * Accessor definition.
     */
    struct _accessor {
      template<class G, class S, class VL>
      _accessor(const std::string &tagname, const G &g, const S &s, const VL &) {
	new_member(tagname, g, s, VL(), 1, 1);
      }
      template<class G, class S, class VL>
      _accessor(const std::string &tagname, const G &g, const S &s, const VL &,
		int minOccurs, int maxOccurs) {
	new_member(tagname, g, s, VL(), minOccurs, maxOccurs);
      }
    private:
      template<class G, class S, class VL> 
      void new_member(const std::string &tagname, 
		      const G &g, const S &s, const VL&,
		      int minOccurs, int maxOccurs) {
	VL::initialize();

	member_getter *mgetter = 
	  create_accessor_getter(reinterpret_cast<T*>(0), 
				 VL::dispatcher_,
				 g, s);	  
	define_member(tagname, 
		      mgetter,
		      VL::dispatcher_,
		      VL::create_default_op(),
		      aka::occurrence(minOccurs, maxOccurs));
      }
    };
    typedef _accessor accessor;
  };
}

#endif
