#include <macros.h>
#include <ArduinoLog.h>
#include "Bridge.h"
#include <Vector.h>
#include <Streaming.h>
#include "constants.h"
#include <StringUtils.h>

#define MAX_COMPONENTS 50
// "send": <<1;2;64;foo:2300:5>>
// #define BRIDGE_DEBUG_REGISTER
#define BRIDGE_DEBUG_CALL_METHOD
#define NB_PAYLOAD_ELEMENTS 4
#define DISABLE_BRIDGE

class SComponentInfo;
SComponentInfo *componentsArray[MAX_COMPONENTS];
Vector<SComponentInfo *> componentList;
Bridge::Bridge(Component *_owner) : Component("Bridge", 0, Component::COMPONENT_DEFAULT, _owner)
{
}
short Bridge::setup()
{
    componentList.setStorage(componentsArray);
    return E_OK;
}

SComponentInfo *Bridge::hasMethod(ushort id, String methodName)
{
    uchar s = componentList.size();
    for (uchar i = 0; i < s; i++)
    {
        SComponentInfo *val = componentList.at(i);
        if (val->key == id && val->methodName.equals(methodName))
        {
            return val;
        }
    }
    return NULL;
}
short Bridge::debug()
{
    return E_OK;
}
short Bridge::list()
{
    uchar s = componentList.size();
    for (uchar i = 0; i < s; i++)
    {
        SComponentInfo *val = componentList.at(i);
        Log.verboseln("\tRegistered Method:  %d::%s::%s", val->key, ((Component *)val->instance)->name.c_str(), val->methodName.c_str());
    }
    return E_OK;
}

SComponentInfo *Bridge::registerMemberFunction(ushort id, Component *clazz, char *method, ComponentFnPtr ptr)
{
    #ifdef DISABLE_BRIDGE
        #pragma message("Bridge::registerMemberFunction : Bridge is disabled!")
        return NULL;
    #endif
#ifndef DISABLE_BRIDGE
    if (!clazz)
    {
        // Log.verboseln("Register member method: %d : invalid class", id);
        return NULL;
    }

    if (!strlen(method))
    {
        // Log.verboseln("Register member method: %d : invalid method name", id);
        return NULL;
    }
    if (!id)
    {
        // Log.verboseln("Register member method: %d : invalid component id", id);
        return NULL;
    }
    if (componentList.size() > MAX_COMPONENTS)
    {
        // Log.errorln("Bridge::registerMemberFunction : Max components reached : %d", MAX_COMPONENTS);
        return NULL;
    }
    SComponentInfo *meth = hasMethod(id, method);
    if (meth)
    {
#ifdef BRIDGE_DEBUG_REGISTER
        Log.verboseln("Register class member: %s::%s already registered!", clazz, method);
#endif
    }
    else
    {
        meth = new SComponentInfo(id, clazz, method, ptr);
        componentList.push_back(meth);
#ifdef BRIDGE_DEBUG_REGISTER
        if (clazz->name)
        {
            Log.verboseln("Register member method: %s (%d)::%s", clazz->name.c_str(), id, method);
        }
        else
        {
            Log.verboseln("Register member method: %d::%s : invalid name", id, method);
        }
#endif
    }
    return meth;
#endif
    return NULL;
}

short Bridge::onMessage(int id, E_CALLS verb, E_MessageFlags flags, const void *user, Component *src)
{

#ifdef BRIDGE_DEBUG_CALL_METHOD
    Log.verboseln("Bridge::onMessage: %d::%d::%d::%s - from ", id, verb, flags, CC_STR(user), src->name.c_str());
#endif

    switch (verb)
    {
    case E_CALLS::EC_USER:
    case E_CALLS::EC_NONE:
    case E_CALLS::EC_COMMAND:
    case E_CALLS::EC_FUNC:
    {
        return E_OK;
    }
    case E_CALLS::EC_METHOD:
    {
        char *strings[4];
        char *ptr = NULL;
        byte index = 0;
        ptr = strtok(C_STR(user), CC_STR(Bridge::METHOD_DELIMITER));

        while (ptr != NULL && index < NB_PAYLOAD_ELEMENTS)
        {
            strings[index] = ptr;
            index++;
            ptr = strtok(NULL, CC_STR(Bridge::METHOD_DELIMITER));
        }

        char *_method = strings[0];

        SComponentInfo *method = hasMethod(id, _method);
        if (method)
        {
            short arg0 = convertTo<short>(CC_STR(strings[1]));
            short arg1 = convertTo<short>(CC_STR(strings[2]));
            Component *component = (Component *)method->instance;
            ComponentFnPtr ptr = method->mPtr;
            short ret = (component->*ptr)(arg0, arg1);
#ifdef BRIDGE_DEBUG_CALL_METHOD
            Log.verboseln("Called method: %s(%d)::%s with : %d | %d = %d", component->name.c_str(), id, _method, arg0, arg1, ret);
#endif
            return ret;
        }
        else
        {
#ifdef BRIDGE_DEBUG_CALL_METHOD
            Log.errorln("Method not found: %d::%s", id, _method);
            list();
#endif
        }
        return E_OK;
    }
    }
    return E_NOT_FOUND;
}
