/*************************************************************************
 * libjson-rpc-cpp
 *************************************************************************
 * @file    cpphelper.cpp
 * @date    29.09.2013
 * @author  Peter Spiess-Knafl <peter.knafl@gmail.com>
 * @license See attached LICENSE.txt
 ************************************************************************/

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

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_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_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_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_STRING:
            result = ".isString()";
            break;
        case JSON_OBJECT:
            result = ".isObject()";
            break;
        case JSON_ARRAY:
            result = ".isArray()";
            break;
    }
    return result;
}
