/********************************************************************
** Copyright (c) 2018-2020 Guan Wenliang
** This file is part of the Berry default interpreter.
** skiars@qq.com, https://github.com/Skiars/berry
** See Copyright Notice in the LICENSE file or at
** https://github.com/Skiars/berry/blob/master/LICENSE
********************************************************************/
#include "be_object.h"
#include "be_exec.h"
#include "be_mem.h"
#include "be_gc.h"
#include "be_vm.h"
#include "be_string.h"

#define cast_comobj(o)      gc_cast(o, BE_COMOBJ, bcommomobj)

const char* be_vtype2str(bvalue *v)
{
    switch(var_primetype(v)) {
    case BE_NIL: return "nil";
    case BE_INT: return "int";
    case BE_REAL: return "real";
    case BE_BOOL: return "bool";
    case BE_CLOSURE: case BE_NTVCLOS: case BE_CTYPE_FUNC:
    case BE_NTVFUNC: return "function";
    case BE_PROTO: return "proto";
    case BE_CLASS: return "class";
    case BE_STRING: return "string";
    case BE_LIST: return "list";
    case BE_MAP: return "map";
    case BE_INSTANCE: return "instance";
    case BE_MODULE: return "module";
    case BE_INDEX: return "var";
    case BE_COMPTR: return "ptr";
    default: return "invalid type";
    }
}

#if BE_USE_PRECOMPILED_OBJECT
extern const bcstring be_const_str_nil;
extern const bcstring be_const_str_int;
extern const bcstring be_const_str_real;
extern const bcstring be_const_str_bool;
extern const bcstring be_const_str_function;
extern const bcstring be_const_str_proto;
extern const bcstring be_const_str_class;
extern const bcstring be_const_str_string;
extern const bcstring be_const_str_list;
extern const bcstring be_const_str_map;
extern const bcstring be_const_str_instance;
extern const bcstring be_const_str_module;
extern const bcstring be_const_str_var;
extern const bcstring be_const_str_ptr;
extern const bcstring be_const_str_invalid_type;
#endif

bstring* be_vtype2bstring(bvalue *v)
{
#if BE_USE_PRECOMPILED_OBJECT
    switch(var_primetype(v)) {
    case BE_NIL: return (bstring*) &be_const_str_nil;
    case BE_INT: return (bstring*) &be_const_str_int;
    case BE_REAL: return (bstring*) &be_const_str_real;
    case BE_BOOL: return (bstring*) &be_const_str_bool;
    case BE_CLOSURE: case BE_NTVCLOS: case BE_CTYPE_FUNC:
    case BE_NTVFUNC: return (bstring*) &be_const_str_function;
    case BE_PROTO: return (bstring*) &be_const_str_proto;
    case BE_CLASS: return (bstring*) &be_const_str_class;
    case BE_STRING: return (bstring*) &be_const_str_string;
    case BE_LIST: return (bstring*) &be_const_str_list;
    case BE_MAP: return (bstring*) &be_const_str_map;
    case BE_INSTANCE: return (bstring*) &be_const_str_instance;
    case BE_MODULE: return (bstring*) &be_const_str_module;
    case BE_INDEX: return (bstring*) &be_const_str_var;
    case BE_COMPTR: return (bstring*) &be_const_str_ptr;
    default: return (bstring*) &be_const_str_invalid_type;
    }
#else
    return be_newstr(vm, be_vtype2str(v));
#endif
}

bvalue* be_indexof(bvm *vm, int idx)
{
    if (idx > 0) { /* absolute index */
        be_assert(vm->reg + idx <= vm->top);
        return vm->reg + idx - 1;
    }
    /* relative index */
    be_assert(vm->top + idx >= vm->reg);
    return vm->top + idx;
}

BERRY_API void be_newcomobj(bvm *vm, void *data, bntvfunc destroy)
{
    bcommomobj *obj;
    bgcobject *gco = be_gcnew(vm, BE_COMOBJ, bcommomobj);
    if ((obj = cast_comobj(gco)) != NULL) {
        bvalue* top = be_incrtop(vm);
        obj->data = data;
        obj->destroy = destroy;
        var_setobj(top, BE_COMOBJ, obj);
    }
}

void be_commonobj_delete(bvm *vm, bgcobject *obj)
{
    bcommomobj *co = cast_comobj(obj);
    if (co) {
        if (co->destroy && co->data) {
            be_pushntvfunction(vm, co->destroy);
            be_pushcomptr(vm, co->data);
            be_call(vm, 1);
            be_pop(vm, 2);
        }
        be_free(vm, co, sizeof(bcommomobj));
    }
}

/* generic destroy method for comobj, just call be_os_free() on the pointer */
int be_commonobj_destroy_generic(bvm* vm)
{
    int argc = be_top(vm);
    if (argc > 0) {
        void * obj = be_tocomptr(vm, 1);
        if (obj != NULL) { be_os_free(obj); }
    }
    be_return_nil(vm);
}
