
#define ICI_CORE
#include "priv.h"

namespace ici
{
    Map* userops = nullptr;

    static String* make_key(const char* t1, const char* binop, const char* t2)
    {
        char buf[100];// really 30 _ max(binop_name) + 30
        const int n = snprintf(buf, sizeof buf, "%s %s %s", t1, binop, t2);
        return String::create(buf, n);
    }

    int define_user_binop(const char* t1, const char* binop, const char* t2, ScriptFunc* fn)
    {
        if(!userops)
        {
            if(!(userops = createMap()))
            {
                return 1;
            }
        }

        Reference<String> k = make_key(t1, binop, t2);
        return userops->assign(k, fn);
    }

    ScriptFunc* lookup_user_binop(const char* t1, const char* binop, const char* t2)
    {
        if(!userops)
        {
            return nullptr;
        }
        Reference<String> k = make_key(t1, binop, t2);
        if(auto r = userops->fetch(k))
        {
            if(isnull(r))
            {
                return nullptr;
            }
            if(r->isScriptFunc())
            {
                return r->toScriptFunc();
            }
            setError("%*s is %s, not a string", k->length(), k->cstr(), r->intern()->name);
        }
        return nullptr;
    }

    Object* call_user_binop(ScriptFunc* fn, Object* lhs, Object* rhs)
    {
        Object* o;
        if(!call(fn, "o=oo", &o, lhs, rhs))
        {
            o = nullptr;
        }
        return o;
    }

    static int cfn_userop_binop()
    {
        char *t1, *binop, *t2;
        ScriptFunc* fn;

        if(!typecheck("ssso", &t1, &binop, &t2, &fn))
        {
            return false;
        }
        if(!fn->isScriptFunc())
        {
            return argerror(3);
        }
        if(define_user_binop(t1, binop, t2, fn))
        {
            return false;
        }
        return setReturnNull();
    }

    WrapFuncs cfuncs_userop()
    {
        static NativeFunc funcs[] =
        {
            ICI_DEFINE_CFUNC(binop, cfn_userop_binop),
        };
        return WrapFuncs(funcs, arraylength(funcs));
    }

}// namespace ici
