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

#include <akaxiso/classes/member.h>
#include <akaxiso/classes/traits.h>
#include <akaxiso/classes/shared_ptr.h>

#include <assert.h>

namespace aka2 {


  class named_member {
  public:
    named_member() : mgetter_(0), default_(0) {}
    named_member(const member_getter *getter) : mgetter_(getter), default_(0){}

    void set_name(const std::string &name){ name_.set(name); }
    const qname &get_name() const  { return name_; }

    bool equals(const void *lhs, const void *rhs) const ;
    void copy(void *dest, const void *src) const;

    memberpair get_member(void *e) const;
    const_memberpair get_member(const void *element) const;

    schematype_id get_schematype() const { 
      return mgetter_->get_op()->get_schematype(); 
    }

    bool is_default_specified() const {
      if (default_.get() == 0)
	return false;
      return default_->has_default(); 
    }

    /** Initialize member value to 
     *    Given default value        : when @default is specified,
     *    Default construction value : when @default is not specified.
     *    Nothing to do              : when value is nill.
     */
    void set_member_to_default(void *e) const {
      assert(get_schematype() == simpletype_id);
      if (default_.get() == 0)
	return;
      void *mem = mgetter_->get_member(e);
      default_->init_value(mem);
    }

    bool is_member_default(const void *e) const { 
      assert(is_default_specified());
      const void *mem = mgetter_->get_member(e);
      return default_->is_default(mem); 
    }

    /**
     * methods when @fixed is specified.
     */
    bool is_fixed_string(const std::string &val) const {
      assert(get_schematype() == fixed_id);
      return default_->is_default_string(val);
    } 

    void write_fixed_string(std::ostream &ostm) const {
      assert(get_schematype() == fixed_id);
      default_->write_default(ostm);
    }

    void set_default_op(default_op *defop) { 
      assert(defop != 0);
      default_ = shared_ptr<default_op>(defop);
    }

  protected:
    qname name_;
    shared_ptr<const member_getter> mgetter_;
    shared_ptr<default_op> default_;
  };


  class member_type : public named_member {
  public:
    member_type() {}
    member_type(const member_getter *getter) : named_member(getter){}
    
    void set_occurrence(const occurrence &occurs) { occurrence_ = occurs; }
    const occurrence& get_occurrence() const { return occurrence_; }
    bool is_element() const { return name_.local()[0] != '&'; }

    void setup_default_value(const char *defval) { 
      default_->set_default(defval); 
    }
  private:
    occurrence occurrence_;
  };



  class attribute_type : public named_member  {
  public:
    attribute_type() : required_(false){}
    attribute_type(const member_getter *mgetter) : 
      named_member(mgetter), required_(false) {    
      schematype_id id = get_schematype();
      if ((id != simpletype_id) &&
	  (id != fixed_id))
	throw internal_error();
    }

    void set_required(bool val) {
      assert(default_.get() != 0); // Simpletype always have default_op.

      if ((!is_default_specified()) && val) {
	// When default is set, use should be 'optional'.
	// Therefore required must not be true.
	throw internal_error();
      }
      required_ = val;
    }

    void setup_default_value(const char *defval) { 
      if (required_)
	throw internal_error();
      default_->set_default(defval); 
    }

    bool is_required() const { return required_; }
    bool should_write(const void *elm) const;
  private:
    bool required_;
  };

  struct member_types : public std::vector<member_type> {};
  struct member_map : public std::map<qname, member_type, qname_less> {};
  struct attribute_types : public std::map<qname, attribute_type, qname_less> {};

  typedef std::map<qname, std::string, qname_less> attribute_values;


  class any_member {
  public:
    any_member() : mgetter_(0) {}
    any_member(const member_getter *getter) : mgetter_(getter) {}

    bool empty() const { return &*mgetter_ == 0; }
    memberpair get_member(void *e) const;
    const_memberpair get_member(const void *element) const;

    schematype_id get_schematype() const { 
      return mgetter_->get_op()->get_schematype(); 
    }

  protected:
    shared_ptr<const member_getter> mgetter_;
  };

}

#endif
