/*************************************************************************
 * libjson-rpc-cpp
 *************************************************************************
 * @file    cpphelper.cpp
 * @date    29.09.2013
 * @author  Peter Spiess-Knafl <dev@spiessknafl.at>
 * @license See attached LICENSE.txt
 ************************************************************************/

#include "cpphelper.h"
#include "../stubgenerator.h"
#include <algorithm>
#include <sstream>

using namespace std;
using namespace jsonrpc;

#define TEMPLATE_CPPSERVER_GUARD1 "#ifndef JSONRPC_CPP_STUB_<STUBNAME>_H_"
#define TEMPLATE_CPPSERVER_GUARD2 "#define JSONRPC_CPP_STUB_<STUBNAME>_H_"

#define TEMPLATE_EPILOG "#endif //JSONRPC_CPP_STUB_<STUBNAME>_H_"

string CPPHelper::toCppType(jsontype_t type, bool isConst, bool isReference) {
  string result;
  switch (type) {
  case JSON_BOOLEAN:
    result = "bool";
    break;
  case JSON_INTEGER:
    result = "int";
    break;
  case JSON_REAL:
    result = "double";
    break;
  case JSON_NUMERIC:
    result = "double";
    break;
  case JSON_STRING:
    result = "std::string";
    break;
  default:
    result = "Json::Value";
    break;
  }
  if (isConst) {
    result = "const " + result;
  }
  if (isReference) {
    result = result + "&";
  }
  return result;
}
string CPPHelper::toCppConversion(jsontype_t type) {
  string result;
  switch (type) {
  case JSON_BOOLEAN:
    result = ".asBool()";
    break;
  case JSON_INTEGER:
    result = ".asInt()";
    break;
  case JSON_REAL:
    result = ".asDouble()";
    break;
  case JSON_NUMERIC:
    result = ".asDouble()";
    break;
  case JSON_STRING:
    result = ".asString()";
    break;
  default:
    result = "";
    break;
  }
  return result;
}
string CPPHelper::toString(jsontype_t type) {
  string result;
  switch (type) {
  case JSON_BOOLEAN:
    result = "jsonrpc::JSON_BOOLEAN";
    break;
  case JSON_INTEGER:
    result = "jsonrpc::JSON_INTEGER";
    break;
  case JSON_REAL:
    result = "jsonrpc::JSON_REAL";
    break;
  case JSON_NUMERIC:
    result = "jsonrpc::JSON_NUMERIC";
    break;
  case JSON_STRING:
    result = "jsonrpc::JSON_STRING";
    break;
  case JSON_OBJECT:
    result = "jsonrpc::JSON_OBJECT";
    break;
  case JSON_ARRAY:
    result = "jsonrpc::JSON_ARRAY";
    break;
  }
  return result;
}
string CPPHelper::generateParameterDeclarationList(Procedure &proc) {
  stringstream param_string;
  parameterNameList_t list = proc.GetParameters();
  for (parameterNameList_t::iterator it = list.begin(); it != list.end();) {
    param_string << toCppParamType(it->second) << " " << it->first;
    if (++it != list.end()) {
      param_string << ", ";
    }
  }
  return param_string.str();
}

string CPPHelper::toCppReturntype(jsontype_t type) { return toCppType(type, false, false); }

string CPPHelper::toCppParamType(jsontype_t type) {
  if (type == JSON_ARRAY || type == JSON_OBJECT || type == JSON_STRING)
    return toCppType(type, true, true);
  else
    return toCppType(type, false, false);
}

string CPPHelper::class2Filename(const string &classname) {
  vector<string> packages = splitPackages(classname);
  string data = packages.at(packages.size() - 1);
  std::transform(data.begin(), data.end(), data.begin(), ::tolower);
  return data + ".h";
}

std::vector<string> CPPHelper::splitPackages(const string &classname) {
  string s = classname;
  string delimiter = "::";
  size_t pos = 0;
  vector<string> tokens;
  while ((pos = s.find(delimiter)) != string::npos) {
    tokens.push_back(s.substr(0, pos));
    s.erase(0, pos + delimiter.length());
  }
  tokens.push_back(s);
  return tokens;
}

void CPPHelper::prolog(CodeGenerator &cg, const string &stubname) {
  cg.writeLine("/**");
  cg.writeLine(" * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!");
  cg.writeLine(" */");
  cg.writeNewLine();

  string stub_upper = stubname;
  std::transform(stub_upper.begin(), stub_upper.end(), stub_upper.begin(), ::toupper);
  StubGenerator::replaceAll2(stub_upper, "::", "_");

  cg.writeLine(StubGenerator::replaceAll(TEMPLATE_CPPSERVER_GUARD1, "<STUBNAME>", stub_upper));
  cg.writeLine(StubGenerator::replaceAll(TEMPLATE_CPPSERVER_GUARD2, "<STUBNAME>", stub_upper));
  cg.writeNewLine();
}

void CPPHelper::epilog(CodeGenerator &cg, const string &stubname) {
  string stub_upper = stubname;
  std::transform(stub_upper.begin(), stub_upper.end(), stub_upper.begin(), ::toupper);
  StubGenerator::replaceAll2(stub_upper, "::", "_");
  cg.writeLine(StubGenerator::replaceAll(TEMPLATE_EPILOG, "<STUBNAME>", stub_upper));
}

int CPPHelper::namespaceOpen(CodeGenerator &cg, const string &classname) {
  vector<string> namespaces = splitPackages(classname);

  for (unsigned int i = 0; i < namespaces.size() - 1; i++) {
    cg.write("namespace ");
    cg.write(namespaces.at(i));
    cg.writeLine(" {");
    cg.increaseIndentation();
  }
  return namespaces.size() - 1;
}

void CPPHelper::namespaceClose(CodeGenerator &cg, int depth) {
  for (int i = 0; i < depth; i++) {
    cg.decreaseIndentation();
    cg.writeLine("}");
  }
}

string CPPHelper::normalizeString(const string &text) {
  string result = text;
  for (unsigned int i = 0; i < text.length(); i++) {
    if (!((text[i] >= 'a' && text[i] <= 'z') || (text[i] >= 'A' && text[i] <= 'Z') || (text[i] >= '0' && text[i] <= '9') || text[i] == '_')) {
      result[i] = '_';
    }
  }
  return result;
}
string CPPHelper::isCppConversion(jsontype_t type) {
  string result;
  switch (type) {
  case JSON_BOOLEAN:
    result = ".isBool()";
    break;
  case JSON_INTEGER:
    result = ".isIntegral()";
    break;
  case JSON_REAL:
    result = ".isDouble()";
    break;
  case JSON_NUMERIC:
    result = ".isNumeric()";
    break;
  case JSON_STRING:
    result = ".isString()";
    break;
  case JSON_OBJECT:
    result = ".isObject()";
    break;
  case JSON_ARRAY:
    result = ".isArray()";
    break;
  }
  return result;
}
