/* -*- 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 {

  struct any_tag { };

  template<class L, class T>
  struct  memberdef {
    static any_tag any;

    // 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);
      }
      template<class P>
      _member(const any_tag &, aka2::any P::* m) {
	new_member("&any", m, any_op(), 1, 1);
      }
      template<class P>
      _member(const any_tag &, aka2::any_array P::* m, int minOccurs, int maxOccurs) {
	new_member("&any[]", m, any_array_op(), 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) {
	schematype_id id = VL::dispatcher_.get_schematype();
	if (!L::acceptable(id))
	  throw internal_error();
      	VL::initialize();

	member_getter *mgetter = 
	  create_ptr_getter(reinterpret_cast<T*>(0), m);	  
	aka2::member_type mtype = aka2::member_type(mgetter, VL::dispatcher_);

	mtype.set_name(tagname);

    	default_op *defop = VL::create_default_op();
	if (defop != 0)
	  mtype.set_default_op(defop);

	if ((minOccurs != 1) || (maxOccurs != 1)) {
	  if ((id == array_id) || (id == choice_id) || (id == any_array_id))
	    mtype.set_occurrence(aka2::occurrence(minOccurs, maxOccurs));
	  else
	    throw internal_error();
	}
	mtype_ = L::register_membertype(mtype);
      }
      member_type *mtype_;
    };
    typedef _member member; // VC6 workaround.


    struct _ptrmember {
      template<class P, class V> 
      _ptrmember(const std::string &tagname, V P::* m, bool required) {
	new_ptr_member(tagname, m, xiso::leaf<TYPENAME V::value_type>(), required);
      }
      template<class P, class V, class VL>
      _ptrmember(const std::string &tagname, V P::* m, const VL&, bool required) {
      	new_ptr_member(tagname, m, VL(), required);
      }
      template<class P>
      _ptrmember(const any_tag &, aka2::deep_ptr<aka2::any> P::* m, bool required) {
      	new_ptr_member("&any[]", m, any_op(), required);
      }
    private:
      template<class P, class V, class VL> 
      void new_ptr_member(const std::string &tagname, V P::* m, const VL&, bool required) {
	VL::initialize();
	member_getter *mgetter = 
	  create_ptr_getter(reinterpret_cast<T*>(0), m);	  
	
	schematype_id id = VL::dispatcher_.get_schematype();
	if (!L::acceptable(id))
	  throw internal_error();
	
	aka2::member_type mtype(mgetter, aka2::ptrmember_op_dispatcher<V, VL>::dispatcher_);
	mtype.set_name(tagname);
	default_op *defop = VL::create_default_op();
	if (defop != 0)
	  mtype.set_default_op(defop);

      	if (required)
	  mtype.set_occurrence(aka2::occurrence(1, 1));
      	else
	  mtype.set_occurrence(aka2::occurrence(0, 1));
	
	L::register_membertype(mtype);
      }
    };
    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();

	aka2::member_type mtype(new null_getter(), aka2::fixed<VL>::dispatcher_);
	mtype.set_name(tagname); 
    	default_op *defop = VL::create_default_op();
	if (defop != 0)
	  mtype.set_default_op(defop);
	L::register_membertype(mtype);
      }
    };

    // 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();
	
	if (VL::dispatcher_.get_schematype() != array_id)
	  throw internal_error();
	
	member_getter *mgetter = create_ptr_getter(static_cast<const T*>(0), m);
	aka2::member_type mtype(mgetter, VL::dispatcher_);
	mtype.set_name(tagname); 
	mtype.set_occurrence(minOccurs, maxOccurs);

	typedef TYPENAME VL::value_leaf_type value_leaf_type;
    	default_op *defop = value_leaf_type::create_default_op();
	if (defop != 0)
	  mtype.set_default_op(defop);
	mtype.setup_default_value(fixed_value);
	
	L::register_membertype(mtype);
      }
    };
    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);	  
	
	schematype_id id = VL::dispatcher_.get_schematype();
	if (!L::acceptable(id))
	  throw internal_error();
	
	aka2::member_type mtype(mgetter, VL::dispatcher_);
	mtype.set_name(tagname);
	default_op *defop = VL::create_default_op();
	if (defop != 0)
	  mtype.set_default_op(defop);

	if ((minOccurs != 1) || (maxOccurs != 1)) {
	  schematype_id id = mtype.get_schematype();
	  if ((id == array_id) || (id == choice_id))
	    mtype.set_occurrence(aka2::occurrence(minOccurs, maxOccurs));
	  else
	    throw internal_error();
	}
	L::register_membertype(mtype);
      }
    };
    typedef _accessor accessor;

  };

  template<class L, class T>
  any_tag memberdef<L, T>::any;

}

#endif
