#include "MinimalFEM/XMLWriter.h"

namespace minimalfem
{

#define HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
#define INDENT "  "
#define NEWLINE "\n"

XMLWriter::XMLWriter(std::ostream& os)
  : os(os)
  , tag_open(false)
  , new_line(true)
{
  os << HEADER;
}

XMLWriter::~XMLWriter() {}

XMLWriter& XMLWriter::openElt(const char* tag)
{
  this->closeTag();
  if (elt_stack.size() > 0)
    os << NEWLINE;
  this->indent();
  this->os << "<" << tag;
  elt_stack.push(tag);
  tag_open = true;
  new_line = false;
  return *this;
}

XMLWriter& XMLWriter::closeElt()
{
  this->closeTag();
  std::string elt = elt_stack.top();
  this->elt_stack.pop();
  if (new_line)
  {
    os << NEWLINE;
    this->indent();
  }
  new_line = true;
  this->os << "</" << elt << ">";
  return *this;
}

XMLWriter& XMLWriter::closeAll()
{
  while (elt_stack.size())
    this->closeElt();
  return *this;
}

XMLWriter& XMLWriter::attr(const char* key, const char* val)
{
  this->os << " " << key << "=\"";
  this->write_escape(val);
  this->os << "\"";
  return *this;
}

XMLWriter& XMLWriter::attr(const char* key, std::string val)
{
  return attr(key, val.c_str());
}

XMLWriter& XMLWriter::content(const char* val)
{
  this->closeTag();
  this->write_escape(val);
  return *this;
}

XMLWriter& XMLWriter::writeContentLine(const char* val)
{
  this->os << val << "\n";
  return *this;
}

void XMLWriter::closeTag()
{
  if (tag_open)
  {
    this->os << ">";
    tag_open = false;
  }
}

void XMLWriter::indent()
{
  for (int i = 0; i < elt_stack.size(); i++)
    os << (INDENT);
}

void XMLWriter::write_escape(const char* str)
{
  for (; *str; str++)
    switch (*str)
    {
      case '&':
        os << "&amp;";
        break;
      case '<':
        os << "&lt;";
        break;
      case '>':
        os << "&gt;";
        break;
      case '\'':
        os << "&apos;";
        break;
      case '"':
        os << "&quot;";
        break;
      default:
        os.put(*str);
        break;
    }
}

} // namespace minimalfem
