/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 * 
 * \file    docutils.cpp
 * \brief   Documentation processing
 * 
 */

#include "docutils.h"

#include <iostream>
#include <fstream>
#include <assert.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

using namespace std;

std::string
getDocumentationExampleById(std::string filename, std::string id) {
  string query = string("(//db:programlisting | //db:code | //db:screen)[@xml:id='") + id + "']";
  xmlDocPtr doc = xmlParseFile(filename.c_str());
  if(!doc) {
    cout << "Can't find documentation file: " << filename << endl;
    assert(false);
    return "";
  }

  xmlXPathContextPtr context = xmlXPathNewContext(doc);
  xmlXPathRegisterNs(context, BAD_CAST "db", BAD_CAST "http://docbook.org/ns/docbook");

  xmlXPathObjectPtr response = xmlXPathEvalExpression((xmlChar*) query.c_str(), context);
  xmlXPathFreeContext(context);
  xmlNodeSetPtr nodeset = response->nodesetval;

  if(nodeset->nodeNr != 1) {
    cout << "Can't find specified example " << id << endl;
    assert(false);
    return "";
  }

  string tag = (char*) nodeset->nodeTab[0]->name;
  bool flagIsPrgListing = tag == "programlisting";
  xmlChar* contentC = xmlNodeListGetString(doc, nodeset->nodeTab[0]->xmlChildrenNode, 1);
  string content = (char*) contentC;
  xmlFree(contentC);
  xmlXPathFreeObject(response);

  //skip first line(used as header)
  if(flagIsPrgListing) {
    content = content.substr(content.find('\n') + 1);
  }

  //replace no break spaces
  replaceAll(content, "\u00A0", " ");

  assert(content.size() && "Empty example");
  return content;
}

std::string
getDocumentationExampleFromFile(std::string filename) {
  std::ifstream stream(filename);
  std::string result;

  stream.seekg(0, std::ios::end);
  result.reserve(stream.tellg());
  stream.seekg(0, std::ios::beg);

  result.assign((std::istreambuf_iterator<char>(stream)),
    std::istreambuf_iterator<char>());

  assert(result.size() && "Empty example");

  return result;
}

void
replace(std::string &str, const std::string &subA, const std::string &subB) {
  size_t posA = str.find(subA);
  assert(posA != string::npos);

  str.replace(posA, subA.size(), subB);
}

void
replaceAll(std::string &str, const std::string &subA, const std::string &subB) {
  size_t posA = str.find(subA);

  while(posA != string::npos) {
    str.replace(posA, subA.size(), subB);
    posA = str.find(subA);
  }
}