/* -*- c++ -*- */
#include "namespaces.h"
#include <cassert>
#include "../platform.h"
#include <algorithm>
#include <sstream>
#include "namespace_statics.h"

using namespace aka2;

const id_type aka2::unregistered_token = -1;
const id_type aka2::xml_ns_token = -2;
const id_type aka2::empty_token = 0;

namespace_map     aka2::namespaces_;
any_namespace_map aka2::any_namespaces_;
prefix_map aka2::pfs_;


#define NS_XML "http://www.w3.org/XML/1998/namespace"

void aka2::xmlns(const std::string &prefix, const std::string &uri) {
  aka2::namespaces_.register_namespace_uri(uri); 
  aka2::pfs_.assign_prefix(prefix, uri); 
}

const std::string &aka2::get_namespace_uri(const std::string &prefix) {
  id_type ns_id = pfs_.get_prefix_id(prefix);
  return get_namespace_uri(ns_id);
}

const std::string &aka2::get_namespace_uri(const id_type ns_id) {
  if (ns_id >= 0x8000)
    return any_namespaces_.get_namespace_uri(ns_id);
  else
    return namespaces_.get_namespace_uri(ns_id);
}

const std::string &aka2::get_prefix(const std::string &uri) {
  aka::id_type ns_id = get_uri_id(uri);
  return pfs_.get_prefix(ns_id);
}

const std::string &aka2::get_prefix(const id_type &ns_id) {
  return pfs_.get_prefix(ns_id);
}

const id_type aka2::get_uri_id(const std::string &uri) {
  id_type ns_id = namespaces_.get_namespace_id(uri);
  if (ns_id == unregistered_token)
    ns_id = any_namespaces_.get_namespace_id(uri);
  return ns_id;
}

const id_type aka2::get_prefix_id(const std::string &prefix) {
  return pfs_.get_prefix_id(prefix);
}



void namespace_map::initialize() {
  if (urimap_.size() != 0)
    return;

  urimap_.insert(urimap_type::value_type(empty_token, ""));
  idmap_.insert(idmap_type::value_type("", empty_token));

  urimap_.insert(urimap_type::value_type(xml_ns_token, NS_XML));
  idmap_.insert(idmap_type::value_type(NS_XML, xml_ns_token));

}

void namespace_map::clear() {
  urimap_.clear();
  idmap_.clear();
}

const id_type namespace_map::register_namespace_uri(const std::string &uri) {
  idmap_type::const_iterator it = idmap_.find(uri);
  if (it != idmap_.end())
    return it->second;

  std::pair<idmap_type::iterator, bool> res_idmap =
    idmap_.insert(idmap_type::value_type(uri, current_id_));
  assert(res_idmap.second);

  std::pair<urimap_type::iterator, bool> res_tokenmap =
    urimap_.insert(urimap_type::value_type(current_id_, uri));
  assert(res_tokenmap.second);

  ++current_id_;
  return res_idmap.first->second;
}


const id_type namespace_map::get_namespace_id(const std::string &uri) const {
  idmap_type::const_iterator it = idmap_.find(uri);
  if (it == idmap_.end())
    return unregistered_token;
  return it->second;
}

const std::string &namespace_map::get_namespace_uri(const id_type id) const {
  urimap_type::const_iterator it = urimap_.find(id);
  assert(it != urimap_.end());
  return it->second;
}


void any_namespace_map::clear() {
  mutex_.lock();
  namespace_map::clear();
  mutex_.unlock();
}


const id_type any_namespace_map::get_namespace_id(const std::string &uri) {
  mutex_.lock();
  id_type id = namespace_map::register_namespace_uri(uri);
  mutex_.unlock();
  return id;
}

const std::string &any_namespace_map::get_namespace_uri(const id_type id) const {
  mutex_.lock();
  const std::string &ret = namespace_map::get_namespace_uri(id);
  mutex_.unlock();
  return ret;
}


const std::string &any_prefix_cache::get_prefix(id_type nsid) const {
  assert(nsid >= 0x8000);
  size_t id = nsid - 0x8000;
  if (id >= cache_.size())
    cache_.resize(id + 1);
  if (cache_[id].empty()) {
    std::ostringstream ostm;
    ostm << prefix_ << id;
    cache_[id] = ostm.rdbuf()->str();
  }
  return cache_[id];
}

const id_type any_prefix_cache::get_namespace_id(const std::string &prefix) const {
  if (prefix.substr(0, prefix_.size()) == prefix)
    return unregistered_token;

  std::istringstream istm(prefix.substr(prefix_.size()));
  id_type id;
  istm >> id;
  if (istm.fail())
    return unregistered_token;

  if (cache_[id] != prefix)
    return unregistered_token;

  id += 0x8000;  
  return id;
}

prefix_map::prefix_map() {
  std::vector<id_type> id_stack;
  id_stack.push_back(empty_token);
  idmap_.insert(idmap_type::value_type("", id_stack));

  prefixes pfs;
  pfs.push_back("");
  prefixmap_.insert(prefixmap_type::value_type(empty_token, pfs));
}

void prefix_map::assign_prefix(const std::string &prefix, const std::string &uri) {

  id_type id = namespaces_.get_namespace_id(uri);
  if ((prefix == xml_prefix_) && (id != xml_ns_token))
    throw error("non-xml namespace URI should not be asociated with \"xml\" prefix.",
		__FILE__, __LINE__);

  if (id == unregistered_token)
    id = any_namespaces_.get_namespace_id(uri);
  if (id == unregistered_token)
    throw error(std::string("Unknown namespace\"") + uri + "\"", __FILE__, __LINE__);
  
  idmap_type::iterator iit = idmap_.find(prefix);
  if (iit != idmap_.end())
    iit->second.push_back(id);
  else {
    std::vector<id_type> id_stack;
    id_stack.push_back(id);
    idmap_.insert(idmap_type::value_type(prefix, id_stack));
  }

  prefixmap_type::iterator pit = prefixmap_.find(id);
  if (pit != prefixmap_.end())
    pit->second.push_back(prefix);
  else {
    prefixes pfs;
    pfs.push_back(prefix);
    prefixmap_.insert(prefixmap_type::value_type(id, pfs));
  }

}

const id_type prefix_map::get_prefix_id(const std::string &prefix) const {

  if (is_prefix_xml(prefix))
    return xml_ns_token;

  idmap_type::const_iterator it = idmap_.find(prefix);
  if (it != idmap_.end()) {
    assert(!it->second.empty());
    return it->second.back();
  }

  return any_cache_.get_namespace_id(prefix);
}

void prefix_map::clear_prefix(const std::string &prefix) {
  if (is_prefix_xml(prefix))
    throw error("xml namespace should not be cleared.",
		__FILE__, __LINE__); // OK? !!!!!!

  idmap_type::iterator itid = idmap_.find(prefix);
  if (itid == idmap_.end()) {
    throw error(std::string("xmlns:") + prefix + " is not registered.",
		__FILE__, __LINE__);
  }

  assert(!itid->second.empty());
  id_type id_to_clear = itid->second.back();
  itid->second.pop_back();
  if (itid->second.empty())
    idmap_.erase(itid);

  prefixmap_type::iterator itprefix = prefixmap_.find(id_to_clear);
  prefixes &pfs = itprefix->second;
  prefixes::iterator it = std::find(pfs.begin(), pfs.end(), prefix);
  assert(it != pfs.end());
  pfs.erase(it);
  if (pfs.empty())
    prefixmap_.erase(itprefix);

}

const std::string &prefix_map::get_prefix(const id_type id) const {
  if (id == xml_ns_token)
    return xml_prefix_;

  if (id < 0x8000) {
    prefixmap_type::const_iterator it = prefixmap_.find(id);
    if (it == prefixmap_.end())
      throw error(std::string("namespace URI(\"") + namespaces_.get_namespace_uri(id)
		  + "\") is not registered.", 
		  __FILE__, __LINE__);
    return it->second.front();
  }
  else {
    return any_cache_.get_prefix(id);
  }
}

bool prefix_map::is_prefix_xml(const std::string &prefix) {
  return stricmp(prefix.c_str(), "xml") == 0;
}

void prefix_map::initialize() {
  xml_prefix_ = "xml";
}

void prefix_map::clear() {
  prefixmap_.clear();
  idmap_.clear();
}

std::string prefix_map::xml_prefix_;
