#include "platform.h"
#include "type_registry.h"
#include "schema.h"
#include "classgen.h"
#include "exception.h"

#include <iostream>

namespace {

  struct type {
    const char *uri;
    const char *name;
    const char *cppname;
  };

  const type types[] = {
    {NS_XMLSCHEMA, "float", "float"},
    {NS_XMLSCHEMA, "double", "double"},
    {NS_XMLSCHEMA, "short", "short"},
    {NS_XMLSCHEMA, "int", "long"},
    {NS_XMLSCHEMA, "string", "std:string"},
    {NULL, NULL, NULL}
  };

  const type array_types[] = {
    {NS_XMLSCHEMA, "float[]", "float_array"},
    {NS_XMLSCHEMA, "double[]", "double_array"},
    {NS_XMLSCHEMA, "short[]", "short_array"},
    {NS_XMLSCHEMA, "int[]", "long_array"},
    {NS_XMLSCHEMA, "string[]", "string_array"},
    {NULL, NULL, NULL}
  };

}

using namespace osixaka;

aka::prefix_map osixaka::cppattrs_;

cppname::cppname(const aka::qname &name) {
  AKA qname::set(name.get_uri_id(), name.local());
}

void cppname::set(const std::string &rawname) {
  AKA qname::set(rawname, cppattrs_);
}

std::string cppname::qualified() const{
  const std::string &pf = prefix(cppattrs_);
  if (pf.empty())
    return local();
  else
    return pf + "::" + local();
}

namespaces osixaka::namespaces_;
resolver osixaka::elements_;
simpleType_resolver osixaka::builtinTypes_;
cpp_resolver osixaka::complexTypes_;
array_resolver osixaka::arrays_;




bool resolver::exists(const aka::qname &name) const {
  name_pairs::const_iterator it = pairs_.find(name);
  return (!(it == pairs_.end()));
}

const aka::qname resolver::resolve(const aka::qname &name) const {
  name_pairs::const_iterator it = pairs_.find(name);
  if (it == pairs_.end()) {
    throw fatal_error(__FILE__, __LINE__, "Could not resolve.");
  }
  return it->second;
}

void resolver::insert(const aka::qname &name, const aka::qname &type) {
  pairs_.insert(name_pairs::value_type(name, type));
}


const cppname cpp_resolver::resolve(const aka::qname &name) const {
  name_pairs::const_iterator it = pairs_.find(name);
  if (it == pairs_.end()) {
    throw fatal_error(__FILE__, __LINE__, "Could not resolve.");
  }
  return cppname(it->second);
}

void cpp_resolver::insert(const aka::qname &name, const cppname &type) {
  pairs_.insert(name_pairs::value_type(name, type));
}


void simpleType_resolver::initialize() {
  for (const type *ty = types; ty->uri != NULL; ++ty) {
    aka::qname name;
    name.set(ty->uri, ty->name);
    pairs_.insert(name_pairs::value_type(name, cppname(ty->cppname)));
  }
}

void array_resolver::initialize() {
  for (const type *ty = array_types; ty->uri != NULL; ++ty) {
    aka::qname name;
    name.set(ty->uri, ty->name);
    pairs_.insert(name_pairs::value_type(name, cppname(ty->cppname)));
  }
}

bool osixaka::type_exists(const aka::qname &type) {
  if (builtinTypes_.exists(type))
    return true;
  if (complexTypes_.exists(type))
    return true;
  if (arrays_.exists(type))
    return true;
  return false;
}

std::string osixaka::get_leaf_name(const aka::qname &xmltype) {
  if (builtinTypes_.exists(xmltype)) {
    const cppname &builtin = builtinTypes_.resolve(xmltype);
    return std::string("xiso::leaf<") + builtin.qualified() + ">";
  }
  else if (arrays_.exists(xmltype))
    return arrays_.resolve(xmltype).qualified() + "_leaf";
  else if (complexTypes_.exists(xmltype))
    return xmltype.qualified() + "_leaf";
  assert(!"Must not reach here.");
  return "";
}


cppname osixaka::get_cpptype(const aka::qname &xmltype) {
  if (builtinTypes_.exists(xmltype))
    return builtinTypes_.resolve(xmltype);
  else if (arrays_.exists(xmltype))
    return arrays_.resolve(xmltype);
  else if (complexTypes_.exists(xmltype)) {
    return cppname(xmltype);
  }
  assert(!"Must not reach here.");
  return cppname();
}

std::ostream &operator<<(std::ostream &ostm, const osixaka::cppname &name) {
  ostm << name.qualified();
  return ostm;
}
