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

#include <akaxiso/schema/schema.h>
#include <set>
#include "exception.h"


template<class T>
struct simple_map {
  typedef std::map<aka::qname, T, aka::qname_less> map_type;
  typedef TYPENAME map_type::iterator iterator;
  typedef TYPENAME map_type::const_iterator const_iterator;
  
  map_type map_;
  
  void insert(const aka::qname &name, const T& t);
  void erase(const aka::qname &name);
  void erase(iterator to_erase) { map_.erase(to_erase); }
  const T& get(const aka::qname &name) const;
  T& get(const aka::qname &name);
  bool exists(const aka::qname &name) const;
  void clear();
  bool empty() const { return map_.empty(); }
  
  iterator begin() { return map_.begin(); }
  iterator end() { return map_.end(); }
  const_iterator begin() const { return map_.begin(); }
  const_iterator end() const { return map_.end(); }
  
};

template<class T>
void simple_map<T>::insert(const aka::qname &name, const T& t) {
  //assert(aka::qname("xs:anyType") != name);
  std::pair<TYPENAME map_type::iterator, bool> res =
    map_.insert(TYPENAME map_type::value_type(name, t));
  if (!res.second)
    throw fatal_error();
}

template<class T>
void simple_map<T>::erase(const aka::qname &name) {
  TYPENAME map_type::iterator it = map_.find(name);
  if (it == map_.end())
    throw fatal_error();
  map_.erase(it);
}

template<class T>
const T& simple_map<T>::get(const aka::qname &name) const {
  TYPENAME map_type::const_iterator it = map_.find(name);
  if (it == map_.end())
    throw fatal_error();
  return it->second;
}

template<class T>
T& simple_map<T>::get(const aka::qname &name) {
  TYPENAME map_type::iterator it = map_.find(name);
  if (it == map_.end()) {
    throw fatal_error();
  }
  return it->second;
}

template<class T>
bool simple_map<T>::exists(const aka::qname &name) const {
  TYPENAME map_type::const_iterator it = map_.find(name);
  return it != map_.end();
}

template<class T>
void simple_map<T>::clear() {
  map_.clear();
}


typedef std::set<aka::qname, aka::qname_less> qname_set;

class class_def;
typedef simple_map<class_def*> class_defs;

/* element */
struct element_type {
  element_type() : is_fixed_(false), is_group_(false), ptrmember_(false) {}
  aka::qname name_;
  aka::qname type_;
  aka::occurrence occ_;
  std::string default_;
  bool is_fixed_;
  bool is_group_;
  bool ptrmember_;
  bool empty() const { return name_.empty() && type_.empty(); }
  bool is_array() const { return (occ_.minOccurs_ != 1) || (occ_.maxOccurs_ != 1); }
  bool is_ptrmember() const { return (occ_.minOccurs_ == 0) && (occ_.maxOccurs_ == 1); }
};

/* attribute */
struct attribute_type {
  attribute_type() : required_(false), is_fixed_(false) {}
  aka::qname name_;
  aka::qname type_;
  bool required_;
  bool is_fixed_;
  std::string default_;
};

struct element_types : std::list<element_type> {
  void check_dup_names() const;
};

typedef std::list<attribute_type> attribute_types;


struct attribute_defs {
  attribute_defs() : has_anyAttribute_(false) {}

  attribute_types attributes_;
  bool has_anyAttribute_;

  bool exists(const aka::qname &name) const;
  void erase(const aka::qname &name);
  attribute_type &get(const aka::qname &name);

  bool has_attributes() const {
    return !attributes_.empty() || has_anyAttribute_;
  }

  void check_dup_names() const;
};

class attributeGroup_def : public attribute_defs {
public:
  aka::qname name_;
};
typedef simple_map<attributeGroup_def> attrGroup_defs;


enum def_type {
  simpletype_id = 1,
  simplecontent_id,
  array_id,
  sequence_id,
  choice_id,
  all_id,
//   enclose_id,
//   disclose_id,
//   any_id,
//   any_array_id,
//   any_attribute_id,
//   fixed_id
};

class class_def {
public:
  class_def(const aka::qname &name, const aka::occurrence &occ, def_type id)
    : name_(name), occ_(occ), id_(id){}

  aka::qname name_;
  aka::occurrence occ_;
  def_type id_;
  class_def *derive(const aka::qname &name) const;
  element_types etypes_;
  aka::qname value_type_;
  attribute_defs adefs_;

  bool is_array() const { return (occ_.minOccurs_ != 1) || (occ_.maxOccurs_ != 1); }
  bool is_ptrmember() const { return occ_.minOccurs_ == 0 && occ_.maxOccurs_ == 1; }
  bool accept_attributes() const { return (id_ == sequence_id) || (id_ == all_id); }
};


class sequence_def : public class_def {
public:
  sequence_def(const aka::qname &name, const aka::occurrence &occ) 
    : class_def(name, occ, sequence_id) {}
};

class all_def : public class_def {
public:
  all_def(const aka::qname &name, const aka::occurrence &occ) 
    : class_def(name, occ, all_id) {}
};

class choice_def : public class_def {
public:
  choice_def(const aka::qname &name, const aka::occurrence &occ)
    : class_def(name, occ, choice_id) {}
};

class simplecontent_def : public class_def {
public:
  simplecontent_def(const aka::qname &name)
    : class_def(name, aka::occurrence(), simplecontent_id) {}
};

class simpletype_def : public class_def {
public:
  simpletype_def(const aka::qname &name)
    : class_def(name, aka::occurrence(), simpletype_id) {}
};


class array_def : public class_def {
public:
  array_def(const aka::qname &name)
    : class_def(name, aka::occurrence(), array_id){}
};


#endif /* CLASSDEFS_H__ */
