#include "registry.h"
//#include "preference.h"

namespace {

  struct ns_decl {
    const char *prefix_;
    const char *uri_;
  };

  ns_decl ns_decls[] = {
    {"wsdl", "http://schemas.xmlsoap.org/wsdl/"},
    {0}
  };
  
  struct type {
    const char *name_;
    const char *cpp_;
    const char *leaf_;
    const char *array_;
    const char *arrayleaf_;
  };

  const type akaxiso_system_builtins[] = {
    // Must not be replaced.
//     {"xs:anyType", "aka:any", 0},
//     {"xs:anySimpleType", "std:string", 0},
    {"aka:any", "aka:any", 0},
    {"aka:any_array", "aka:any_array", 0},
    {0}
  };

  const type akaxiso_builtins[] = {
    {"xs:byte",          "char",          0, "aka:char_array",      0},
    {"xs:unsignedByte",  "unsigned char", 0, "aka:uchar_array",     0},
    {"xs:short",         "short",         0, "aka:short_array",     0},
    {"xs:unsignedShort", "short",         0, "aka:short_array",     0},
    {"xs:int",           "long",          0, "aka:long_array",      0},
    {"xs:unsignedInt",   "ULONGLONG",     0, "aka:longlong_array",  0},
    {"xs:long",          "LONGLONG",      0, "aka:longlong_array",  0},
    {"xs:unsignedLong",  "ULONGLONG",     0, "aka:ulonglong_array", 0},
    {"xs:integer",       "long",          0, "aka:long_array",      0},
    {"xs:boolean",       "bool",          0, "aka:bool_array",      0},
    {"xs:float",         "float",         0, "aka:float_array",     0},
    {"xs:double",        "double",        0, "aka:double_array",    0},
    {"xs:string",        "std:string",    0, "aka:string_array",    0},
    {"xs:anySimpleType", "std:string",    0, "aka:string_array",    0},
    // empty-value type provided by akaxiso. 
    {"aka:nill",         "aka:nill",      0, "aka:nill_array",      0}, 
    {0}
  };

  const type xs_builtins[] = {
    {"xs:normalizedString",   "std:string", 0,              "xs:normalizedString_array",   0},
    {"xs:token",              "std:string", 0,              "xs:token_array",              0},
    {"xs:base64Binary",       "std:string", 0,              "xs:base64Binary_array",       0},
    {"xs:hexBinary",          "std:string", 0,              "xs:hexBinary_array",          0},
    {"xs:positiveInteger",    "long",       0,              "xs:positiveInteger_array",    0},
    {"xs:negativeInteger",    "long",       0,              "xs:negativeInteger_array",    0},
    {"xs:nonNegativeInteger", "long",       0,              "xs:nonNegativeInteger_array", 0},
    {"xs:nonPositiveInteger", "long",       0,              "xs:nonPositiveInteger_array", 0},
    {"xs:decimal",            "long",       0,              "xs:decimal_array",            0},
    {"xs:time",               "std:string", 0,              "xs:time_array",               0},
    {"xs:dateTime",           "std:string", 0,              "xs:dateTime_array",           0},
    {"xs:duration",           "std:string", 0,              "xs:duration_array",           0},
    {"xs:date",               "std:string", 0,              "xs:date_array",               0},
    {"xs:gMonth",             "std:string", 0,              "xs:gMonth_array",             0},
    {"xs:gYear",              "std:string", 0,              "xs:gYear_array",              0},
    {"xs:gYearMonth",         "std:string", 0,              "xs:gYearMonth_array",         0},
    {"xs:gDay",               "std:string", 0,              "xs:gDay_array",               0},
    {"xs:gMonthDay",          "std:string", 0,              "xs:gMonthDay_array",          0},
    {"xs:Name",               "std:string", 0,              "xs:Name_array",               0},
    {"xs:QName",              "aka:qname",  "xs:QNameLeaf", "xs:QName_array",              0},
    {"xs:NCName",             "std:string", 0,              "xs:NCName_array",             0},
    {"xs:anyURI",             "std:string", 0,              "xs:anyURI_array",             0},
    {"xs:language",           "std:string", 0,              "xs:language_array",           0},
    {"xs:ID",                 "std:string", 0,              "xs:ID_array",                 0},
    {"xs:IDREF",              "std:string", 0,              "xs:IDREF_array",              0},
    {"xs:IDREFS",             "std:string", 0,              "xs:IDREFS_array",             0},
    {"xs:ENTITY",             "std:string", 0,              "xs:ENTITY_array",             0},
    {"xs:ENTITIES",           "std:string", 0,              "xs:ENTITIES_array",           0},
    {"xs:NOTATION",           "std:string", 0,              "xs:NOTATION_array",           0},
    {"xs:NMTOKEN",            "std:string", 0,              "xs:NHMTOKEN_array",           0},
    {"xs:NMTOKENS",           "std:string", 0,              "xs:NMTOKENS_array",           0},
    {0}
  };

  struct token_substitution {
    const char *original;
    const char *substituted;
  };

  const token_substitution substitutions[] = {
    {"class",     "_class"},
    {"public",    "_public"},
    {"protected", "_protected"},
    {"private",   "_private"},
    {"union",     "_union"},
    {"enum",      "_enum"},
    {"namespace", "_namespace"},
    {"using",     "_using"},
    {0}
  };
  
  struct escape_pair {
    const char *to_escape_;
    const char *escaped_; 
  };

  const escape_pair escape_pairs[] = {
    {"-", "_"},
    {0}
  };



  std::string to_cppname(const std::string &name) {
    aka::qname qn(name);
    if (qn.is_qualified())
      return qn.prefix() + "::" + qn.local();
    return qn.local();
  }
}


bool registry::type_exists(const aka::qname &type) const {
  return attributeGroups_.exists(type) || classes_.exists(type);
}

bool registry::name_exists(const aka::qname &name) const {
  return elements_.exists(name)
    || attributes_.exists(name)
    || simpleTypes_.exists(name);
}


void registry::get_predefinedTypes(qname_set &qnames) const {
  const type *ty;
  for (ty = akaxiso_system_builtins; ty->name_ != 0; ++ty) {
    qnames.insert(aka::qname(ty->name_));
  }
  for (ty = akaxiso_builtins; ty->name_ != 0; ++ty) {
    qnames.insert(aka::qname(ty->name_));
    qnames.insert(aka::qname(ty->array_));
  }
  for (ty = xs_builtins; ty->name_ != 0; ++ty) {
    qnames.insert(aka::qname(ty->name_));
    qnames.insert(aka::qname(ty->array_));
  }
}


void registry::insert_simpleTypes() {
  const type *ty;
  for (ty = akaxiso_builtins; ty->name_ != 0; ++ty)
    simpleTypes_.replace(aka::qname(ty->name_), aka::qname(ty->cpp_));
  for (ty = xs_builtins; ty->name_ != 0; ++ty)
    simpleTypes_.replace(aka::qname(ty->name_), aka::qname(ty->cpp_));
}

aka::qname registry::rename_type(const aka::qname &tyname) const {
  const type *ty;
  for (ty = akaxiso_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == tyname)
      return aka::qname(ty->cpp_);
  }
//   for (ty = xs_builtins; ty->name != 0; ++ty) {
//     if (aka::qname(ty->name) == tyname)
//       return aka::qname(ty->cppname);
//   }
  for (const token_substitution *sub = substitutions; sub->original != 0; ++sub) {
    if (tyname.local() == sub->original)
      return aka::qname(tyname.get_uri_id(), sub->substituted);
  }
  return tyname;
}


bool registry::is_predefined(const aka::qname &tyname) const {
  const type *ty;
  for (ty = akaxiso_system_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == tyname)
      return true;
  }
  for (ty = akaxiso_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == tyname)
      return true;
  }
  for (ty = xs_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == tyname)
      return true;
  }
  return false;
}

bool registry::is_builtin(const aka::qname &tyname) const {
  const type *ty;
  for (ty = akaxiso_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == tyname)
      return true;
  }
  return false;
}

std::string registry::get_predefined_leafname(const aka::qname &tyname) const {
  const type *ty;
  for (ty = akaxiso_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == tyname) {
      if (ty->leaf_ != 0)
	return ty->leaf_;
      return std::string("xiso::leaf<") + to_cppname(ty->cpp_) + ">";
    }
    else if (aka::qname(ty->array_) == tyname) {
      if (ty->arrayleaf_ != 0)
	return ty->arrayleaf_;
      return to_cppname(ty->array_) + "_leaf";
    }
  }
  for (ty = xs_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == tyname) {
      if (ty->leaf_ != 0)
	return to_cppname(ty->leaf_);
      return std::string("xiso::leaf<") + to_cppname(ty->cpp_) + ">";
    }
    else if (aka::qname(ty->array_) == tyname) {
      if (ty->arrayleaf_ != 0)
	return to_cppname(ty->arrayleaf_);
      return to_cppname(ty->array_) + "_leaf";
    }
  }
  assert(!"Must not reach here.");
  return std::string();
}


bool registry::is_toplevel(const aka::qname &name) const {
  return toplevels_.find(name) != toplevels_.end();
}

aka::qname registry::get_predefined_array(const aka::qname &name) const {
  const type* ty;
  for (ty = akaxiso_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == name)
      return aka::qname(ty->array_);
  }
  for (ty = xs_builtins; ty->name_ != 0; ++ty) {
    if (aka::qname(ty->name_) == name)
      return aka::qname(ty->array_);
  }
  assert(!"Must not reach here.");
  return aka::qname();
}

bool registry::is_any(const aka::qname &name) const {
  return name == aka::qname("aka:any")
    || name == aka::qname("aka:any_array");
}

std::string registry::escape(const std::string &to_escape) const {
  std::string escaped = to_escape;
  
  for (const escape_pair *es = escape_pairs; es->to_escape_ != 0; ++es) {
    bool replaced;
    do {
      replaced = false;
      std::string::size_type pos = escaped.find(es->to_escape_);
      if (pos != std::string::npos) {
	std::string::size_type len = std::string(es->to_escape_).size();
	escaped = escaped.substr(0, pos) + es->escaped_ + 
	  escaped.substr(pos + len);
	replaced = true;
      }
    } while (replaced);
  } 
  return escaped;
}



// void registry::save_preference() {
  
//   osx::preference pref;

//   osx::namespace_array &nss = pref.namespaces_.namespace_;
//   for (const ns_decl* decl = ns_decls; decl->prefix_ != 0; ++decl) {
//     osx::_namespace ns;
//     ns.prefix_ = decl->prefix_;
//     ns.uri_ = decl->uri_;
//     nss.push_back(ns);
//   }

//   const type *ty;

//   osx::type_array &abuitins = pref.akaxiso_builtins_.type_;
//   for (ty = akaxiso_builtins; ty->name_ != 0; ++ty) {
//     osx::type t;
//     t.schema_ = aka::qname(ty->name_);
//     t.cpp_ = aka::qname(ty->cpp_);
//     if (ty->leaf_ != 0)
//       t.xiso_ = aka::qname(ty->leaf_);
//     abuitins.push_back(t);
//   }

//   osx::type_array &xsbuiltins = pref.schema_builtins_.type_;
//   for (ty = xs_builtins; ty->name_ != 0; ++ty) {
//     osx::type t;
//     t.schema_ = aka::qname(ty->name_);
//     t.cpp_ = aka::qname(ty->cpp_);
//     if (ty->leaf_ != 0)
//       t.xiso_ = aka::qname(ty->leaf_);
//     xsbuiltins.push_back(t);
//   }
// }

void registry::register_ns() {
  for (ns_decl *decl = ns_decls; decl->prefix_ != 0; ++decl)
    aka::xmlns(decl->prefix_, decl->uri_);
}
