#include "ygg_handler.h"
#include "../classes/document.h"
#include "../parser/sequence_handler.h"
#include "../parser/choice_handler.h"
#include "../classes/namespace_statics.h"

using namespace aka2;
namespace ygg = yggdrasil; 

#ifdef _MSC_VER
#include <malloc.h>
#endif


namespace {
  std::string trim(const std::string &str) {
    std::string::size_type begin_pos = str.find_first_not_of(' ');
    std::string::size_type end_pos = str.find_last_not_of(' ');
    return str.substr(begin_pos, end_pos - begin_pos + 1);
  }
  
  std::string trim_left(const std::string &str) {
    std::string::size_type begin_pos = str.find_first_not_of(' ');
    if (begin_pos == std::string::npos)
      return str;
    else
      return str.substr(begin_pos);
  }
}


void ns_stack::inc_depth() {
  ns_decls decls;
  if (!stack_.empty())
    decls.default_ns_id_ = stack_.top().default_ns_id_;
  stack_.push(decls);
}

void ns_stack::dec_depth() {
  stack_.pop();
}

const ns_decls &ns_stack::get_current_decls() const {
  return stack_.top();
}

bool ns_stack::process_namespace_attribute(const ygg::ygg_node &node, const nsdecl **decl) {

  std::string attrname = trim(node.get_name());
  if (attrname.find("xmlns", 0, 5) != 0) // is case sensitive?
    return false;

  std::string uri = node.get_value().c_str();
  if ((uri.at(0) == '"') && (uri.at(uri.size() - 1) == '"')){
    uri.erase(0, 1);                // remove ""
    uri.erase(uri.length() - 1, 1);
  }
  id_type uriid = namespaces_.get_namespace_id(uri); // allow dynamic registration of NS ? !!!!!

  nsdecl newentry;
  newentry.uri_ = uri;
  if (attrname.size() == 5) { // length of "xmlns"
    stack_.top().default_ns_id_ = uriid;
    newentry.prefix_ = "";
  }
  else if (attrname.find("xmlns:", 0, 6) == 0) {
    newentry.prefix_ = attrname.substr(6); // check space included? !!!!!
  }

  stack_.top().prefixes_.push_back(newentry);
  *decl = &stack_.top().prefixes_.back();
  return true;

}


ygg_handler::ygg_handler() : handler_(pfs_) {
}


void ygg_handler::process_attributes(const yggdrasil::ygg_node &elm, attribute_values &attrs) {

  // Check prefix
  ygg::ygg_node yattrs = elm["@*"];
  
  for (int i = 0; i < yattrs.get_size(); ++i) {
    const nsdecl *decl = 0; 
    ygg::ygg_node node = yattrs[i];
    if (ns_.process_namespace_attribute(node, &decl)) {
      if (decl != 0)
  start_prefix_mapping(decl->prefix_, decl->uri_);
    }
    else {
      qname name;
      name.set(node.get_name(), pfs_);  
      std::pair<attribute_values::iterator, bool> res = 
  attrs.insert(attribute_values::value_type(name, node.get_value()));
      if (!res.second){
  throw parse_error("Attributes \"" + node.get_name() + "\" appeared more than twice.",
        __FILE__, __LINE__);
      }
    }
  }
}


void ygg_handler::start_element(ygg::ygg_node elm) {

  attribute_values xattrs;

  ns_.inc_depth();

  if (handler_.get_context().empty()) {
    if (elm.get_name() == "?xml")
      return;
    process_attributes(elm, xattrs);
    qname name = create_qname(elm.get_name());;
    handler_.startElement(name, xattrs);
    return;
  }

  process_attributes(elm, xattrs);
  qname name = create_qname(elm.get_name());
  handler_.startElement(name, xattrs);
}


void ygg_handler::end_element(ygg::ygg_node node){

  if (node.get_name() == "?xml")
    return;

  qname name = create_qname(node.get_name());

  std::string chars = node.get_value();
  handler_.characters(chars.c_str(), chars.size());

  handler_.endElement(name);

  const ns_decls &nsdecls = ns_.get_current_decls();
  const ns_decls::prefixes &prfs = nsdecls.prefixes_;
  for (ns_decls::prefixes::const_iterator it = prfs.begin(); it != prfs.end(); ++it)
    end_prefix_mapping(it->prefix_);

  ns_.dec_depth();

}

void ygg_handler::on_error(ygg::ygg_error* er) {
  throw parse_error(er->get_message(), __FILE__, __LINE__);
}

document ygg_handler::get_document(){
  return handler_.get_document();
}

void ygg_handler::start_prefix_mapping(const std::string &prefix, const std::string &uri) {
  handler_.start_prefix_mapping(prefix, uri);
}

void ygg_handler::end_prefix_mapping(const std::string &prefix){
  handler_.end_prefix_mapping(prefix);
}

qname ygg_handler::create_qname(const std::string &tagname) {
  assert(!tagname.empty());
  std::string trimmed = trim(tagname);
  qname name;
  if (trimmed.find_first_of(':') == std::string::npos) {
    name.set(ns_.get_current_decls().default_ns_id_, trimmed);
  }
  else {
    name.set(trimmed, pfs_);
  }
  return name;
}
