/*
 * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "libsmwrapper.h"
using namespace std;

static int create_json_object(const QJsonObject& obj, struct json_object* jobj);
static bool put_val_to_jobj(const char* key, const QJsonValue& val, struct json_object* jobj);
static bool put_array_to_jobj(const char* key, const QJsonArray& qarray, struct json_object* jobj);

LibSMWrapper::LibSMWrapper(QObject *parent) :
    QObject(parent)
{
    /* This is not enabled */
    //libsm = new LibSoundmanager();
}

LibSMWrapper::LibSMWrapper(const int port, const QString& token, QObject *parent) :
    QObject(parent)
{
    libsm = new LibSoundmanager(port, token.toStdString());
}

LibSMWrapper::~LibSMWrapper()
{
    delete libsm;
}

int LibSMWrapper::call(const QString &verb, const QString &arg)
{
    // translate QJsonObject to struct json_object
    struct json_object* jobj = json_object_new_object();
    QJsonDocument jsonDoc = QJsonDocument::fromJson(arg.toUtf8());
    const QJsonObject jsonObj = jsonDoc.object();
    int ret = create_json_object(jsonObj, jobj);
    if(ret < 0)
    {
        return -1;
    }
    return libsm->call(verb.toStdString().c_str(), jobj);
}

static int create_json_object(const QJsonObject& obj, struct json_object* jobj)
{
    try{
        for(auto itr = obj.begin(); itr != obj.end();++itr)
        {
            string key = itr.key().toStdString();
            //const char* key = itr.key().toStdString().c_str(); // Do not code like this. string is removed if size is over 16!!

            bool ret = put_val_to_jobj(key.c_str(), itr.value(),jobj);
            if(!ret){
                /*This is not implemented*/
                qDebug("JsonArray can't parse for now");
                return -1;
                // ToDo 
                // For now, array may not be inputted for soundmanager
                // But use case absolutely exists
                /*QJsonArray qarray = itr.value().toArray();
                ret = put_array_to_jobj(key, qarray, jobj);*/
            }
        }
    }
    catch(...){
        qDebug("Json parse error occured");
        return -1;
    }
    return 0;
}

static bool put_val_to_jobj(const char* key, const QJsonValue& val, struct json_object* jobj)
{
    if(val.isArray()){
        return false;  // Array can't input
    }
    if(val.isString()){
        string value = val.toString().toStdString();
        json_object_object_add(jobj, key, json_object_new_string(value.c_str()));
    }
    else{
        const int value = val.toInt();     
        json_object_object_add(jobj, key, json_object_new_int(value));   
    }
    return true;
}

static bool put_array_to_jobj(const char* key, const QJsonArray& qarray, struct json_object* jobj)
{
    // ToDo Fix this !!
/*    struct json_object* jarray = json_object_new_array();
    
    bool ret;
    for(auto jitr = qarray.begin(); jitr != qarray.end(); ++jitr){
        struct json_object* tmp = json_object_new_object();
        ret = put_val_to_jobj(key,jitr,tmp);
        if(!ret)
        {
            put_array_to_jobj(key,jitr,tmp);
        }
        json_object_array_add(jarray, tmp);
    }
    json_object_object_add(jobj, key, jarray);
    return true;*/
}

void LibSMWrapper::wrapper_registerCallback(
    void (*event_func)(const string& event, struct json_object* event_contents), 
    void (*reply_func)(struct json_object* reply_contents))
{
    libsm->register_callback(event_func, reply_func);
}

void LibSMWrapper::subscribe(const QString event_name)
{
    std::string str = event_name.toStdString();
    libsm->subscribe(str);
}

void LibSMWrapper::unsubscribe(const QString event_name)
{
    std::string str = event_name.toStdString();
    libsm->unsubscribe(str);
}

void LibSMWrapper::run_eventloop()
{
    libsm->run_eventloop();
}

void LibSMWrapper::print(const QString &str)
{
    qDebug("%s is called", str.toStdString().c_str());
}

void LibSMWrapper::emit_event(const QString &event, const QJsonObject &msg)
{
    qDebug("emit smEvent signal @%s", __FUNCTION__);
    emit smEvent(event, msg);
}
void LibSMWrapper::emit_reply(const QJsonObject &msg)
{
    qDebug("emit smReply signal @%s", __FUNCTION__);    
    emit smReply(msg);
}