/// Index a document.
bool ElasticSearch::index(const std::string& index, const std::string& type, const std::string& id, const Json::Object& jData){

    if(_readOnly)
        return false;

    std::stringstream url;
    url << index << "/" << type << "/" << id;

    std::stringstream data;
    data << jData;

    Json::Object result;
    _http.put(url.str().c_str(), data.str().c_str(), &result);

    if(!result.member("created"))
        EXCEPTION("The index induces error.");

    if(result.getValue("created"))
        return true;

    std::cout << "endPoint: " << index << "/" << type << "/" << id << std::endl;
    std::cout << "jData" << jData.pretty() << std::endl;
    std::cout << "result" << result.pretty() << std::endl;

    EXCEPTION("The index returns ok: false.");
    return false;
}
/// Delete the document by index/type/id.
bool ElasticSearch::deleteDocument(const char* index, const char* type, const char* id){
    if(_readOnly)
        return false;

    std::ostringstream oss;
    oss << index << "/" << type << "/" << id;
    Json::Object msg;
    _http.remove(oss.str().c_str(), 0, &msg);

    return msg.getValue("found");
}
/// Search API of ES.
long ElasticSearch::search(const std::string& index, const std::string& type, const std::string& query, Json::Object& result, const int n_result=10){

    std::stringstream url;
    url << index << "/" << type << "/_search?size=" << n_result;

    _http.post(url.str().c_str(), query.c_str(), &result);

    if(!result.member("timed_out")){
        std::cout << url.str() << " -d " << query << std::endl;
        std::cout << "result: " << result << std::endl;
        EXCEPTION("Search failed.");
    }

    if(result.getValue("timed_out")){
        std::cout << "result: " << result << std::endl;
        EXCEPTION("Search timed out.");
    }

    return result.getValue("hits").getObject().getValue("total").getLong();
}
//retrive all documemnts of a class
int ElasticSearch::fullScan(const std::string& index, const std::string& type, const std::string& query, Json::Array& resultArray, int scrollSize) {

    // Get the scroll id
    std::stringstream scrollUrl;
    scrollUrl << index << "/" << type << "/_search?search_type=scan&scroll=10m&size=" << scrollSize;

    Json::Object scrollObject;
    _http.post(scrollUrl.str().c_str(),query.c_str(),&scrollObject);

    if(!scrollObject.member("hits"))
        EXCEPTION("Result corrupted, no member \"hits\".");

    if(!scrollObject.getValue("hits").getObject().member("total"))
        EXCEPTION("Result corrupted, no member \"total\" nested in \"hits\".");

    int total = scrollObject.getValue("hits").getObject().getValue("total").getInt();

    std::string scrollId = scrollObject["_scroll_id"].getString();
    int count = 0;
    while(count < total) {

        Json::Object result;
        _http.rawpost("_search/scroll?scroll=10m", scrollId.c_str(), &result);

        // Kepp the new scroll id we received to inject in the next iteration.
        scrollId = result["_scroll_id"].getString();

        for(const Json::Value& value : result["hits"].getObject()["hits"].getArray()){
            resultArray.addElement(value);
            ++count;
        }
    }

    if(count != total)
        EXCEPTION("Result corrupted, total is different from count.");

    return total;
}
/// Index a document with automatic id creation
std::string ElasticSearch::index(const std::string& index, const std::string& type, const Json::Object& jData){

    if(_readOnly)
        return "";

    std::stringstream url;
    url << index << "/" << type << "/";

    std::stringstream data;
    data << jData;

    Json::Object result;
    _http.post(url.str().c_str(), data.str().c_str(), &result);

    if(!result.member("created") || !result.getValue("created")){
        std::cout << "url: " << url.str() << std::endl;
        std::cout << "data: " << data.str() << std::endl;
        std::cout << "result: " << result.str() << std::endl;
        EXCEPTION("The index induces error.");
    }

    return result.getValue("_id").getString();
}
// Test if document exists
bool ElasticSearch::exist(const std::string& index, const std::string& type, const std::string& id){
    std::stringstream url;
    url << index << "/" << type << "/" << id;

    Json::Object result;
    _http.get(url.str().c_str(), 0, &result);

    if(!result.member("found")){
        std::cout << result << std::endl;
        EXCEPTION("Database exception, field \"found\" must exist.");
    }

    return result.getValue("found");
}
// Request the document number of type T in index I.
long unsigned int ElasticSearch::getDocumentCount(const char* index, const char* type){
    std::ostringstream oss;
    oss << index << "/" << type << "/_count";
    Json::Object msg;
    _http.get(oss.str().c_str(),0,&msg);

    size_t pos = 0;
    if(msg.member("count"))
        pos = msg.getValue("count").getUnsignedInt();
    else
        printf("We did not find \"count\" member.\n");

    return pos;
}