/*  see copyright notice in squirrel.h */
#ifndef _SQCLOSURE_H_
#define _SQCLOSURE_H_


#define _CALC_CLOSURE_SIZE(func) (sizeof(SQClosure) + (func->_noutervalues*sizeof(SQObjectPtr)) + (func->_ndefaultparams*sizeof(SQObjectPtr)))

struct SQFunctionProto;
struct SQClass;
struct SQClosure : public CHAINABLE_OBJ
{
private:
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
     SQClosure(SQSharedState *ss,SQFunctionProto *func){_function = func; __ObjAddRef(_function); _base = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL; _root=NULL;}
public:
    static SQClosure  *Create(SQSharedState *ss,SQFunctionProto *func,SQWeakRef *root){
#else
     SQClosure(SQFunctionProto *func){_function = func; __ObjAddRef(_function); _base = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL; _root=NULL;}
public:
    static SQClosure  *Create(SQFunctionProto *func,SQWeakRef *root){
#endif
        SQInteger size = _CALC_CLOSURE_SIZE(func);
        SQClosure *nc=(SQClosure*)SQ_MALLOC(size);
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
        new (nc) SQClosure(ss,func);
#else
        new (nc) SQClosure(func);
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
        nc->_outervalues = (SQObjectPtr *)(nc + 1);
#pragma GCC diagnostic pop
        nc->_defaultparams = &nc->_outervalues[func->_noutervalues];
        nc->_root = root;
         __ObjAddRef(nc->_root);
        _CONSTRUCT_VECTOR(SQObjectPtr,func->_noutervalues,nc->_outervalues);
        _CONSTRUCT_VECTOR(SQObjectPtr,func->_ndefaultparams,nc->_defaultparams);
        return nc;
    }
    void  Release(){
        SQFunctionProto *f = _function;
        SQInteger size = _CALC_CLOSURE_SIZE(f);
        _DESTRUCT_VECTOR(SQObjectPtr,f->_noutervalues,_outervalues);
        _DESTRUCT_VECTOR(SQObjectPtr,f->_ndefaultparams,_defaultparams);
        __ObjRelease(_function);
        this->~SQClosure();
        sq_vm_free(this,size);
    }
    void  SetRoot(SQWeakRef *r)
    {
        __ObjRelease(_root);
        _root = r;
        __ObjAddRef(_root);
    }
    SQClosure  *Clone()
    {
        SQFunctionProto *f = _function;
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
        SQClosure * ret = SQClosure::Create(_opt_ss(this),f,_root);
#else
        SQClosure * ret = SQClosure::Create(f,_root);
#endif
        ret->_env = _env;
        if(ret->_env) __ObjAddRef(ret->_env);
        _COPY_VECTOR(ret->_outervalues,_outervalues,f->_noutervalues);
        _COPY_VECTOR(ret->_defaultparams,_defaultparams,f->_ndefaultparams);
        return ret;
    }
    ~SQClosure();

    bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) ;
    static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) ;
#ifndef NO_GARBAGE_COLLECTOR
    void Mark(SQCollectable **chain) ;
    void  Finalize(){
        SQFunctionProto *f = _function;
        _NULL_SQOBJECT_VECTOR(_outervalues,f->_noutervalues);
        _NULL_SQOBJECT_VECTOR(_defaultparams,f->_ndefaultparams);
    }
    SQObjectType  GetType() {return OT_CLOSURE;}
#endif

    SQWeakRef *_env;
    SQWeakRef *_root;
    SQClass *_base;
    SQFunctionProto *_function;
    SQObjectPtr *_outervalues;
    SQObjectPtr *_defaultparams;
};

//////////////////////////////////////////////
struct SQOuter : public CHAINABLE_OBJ
{

private:
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
     SQOuter(SQSharedState *ss, SQObjectPtr *outer){_valptr = outer; _next = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); }

public:
    static  SQOuter *Create(SQSharedState *ss, SQObjectPtr *outer)
    {
        SQOuter *nc  = (SQOuter*)SQ_MALLOC(sizeof(SQOuter));
        new (nc) SQOuter(ss, outer);
#else
     SQOuter(SQObjectPtr *outer){_valptr = outer; _next = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); }

public:
    static  SQOuter *Create(SQObjectPtr *outer)
    {
        SQOuter *nc  = (SQOuter*)SQ_MALLOC(sizeof(SQOuter));
        new (nc) SQOuter(outer);
#endif
        return nc;
    }
     ~SQOuter() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); }

    void  Release()
    {
        this->~SQOuter();
        sq_vm_free(this,sizeof(SQOuter));
    }

#ifndef NO_GARBAGE_COLLECTOR
    void Mark(SQCollectable **chain) ;
    void  Finalize() { _value.Null(); }
    SQObjectType  GetType() {return OT_OUTER;}
#endif

    SQObjectPtr *_valptr;  /* pointer to value on stack, or _value below */
    SQInteger    _idx;     /* idx in stack array, for relocation */
    SQObjectPtr  _value;   /* value of outer after stack frame is closed */
    SQOuter     *_next;    /* pointer to next outer when frame is open   */
};

//////////////////////////////////////////////
struct SQGenerator : public CHAINABLE_OBJ
{
    enum SQGeneratorState{eRunning,eSuspended,eDead};
private:
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
     SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=NULL;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
public:
    static SQGenerator  *Create(SQSharedState *ss,SQClosure *closure){
        SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator));
        new (nc) SQGenerator(ss,closure);
#else
     SQGenerator(SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=NULL;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
public:
    static  SQGenerator *Create(SQClosure *closure){
        SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator));
        new (nc) SQGenerator(closure);
#endif
        return nc;
    }
     ~SQGenerator()
    {
        REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
    }

    void  Kill(){
        _state=eDead;
        _stack.resize(0);
        _closure.Null();}
    void  Release(){
        sq_delete(this,SQGenerator);
    }

    bool Yield(SQVM *v,SQInteger target) ;
    bool Resume(SQVM *v,SQObjectPtr &dest) ;
#ifndef NO_GARBAGE_COLLECTOR
    void Mark(SQCollectable **chain) ;
    void  Finalize(){_stack.resize(0);_closure.Null();}
    SQObjectType  GetType() {return OT_GENERATOR;}
#endif
    SQObjectPtr _closure;
    SQObjectPtrVec _stack;
    SQVM::CallInfo _ci;
    ExceptionsTraps _etraps;
    SQGeneratorState _state;
};

#define _CALC_NATVIVECLOSURE_SIZE(noutervalues) (sizeof(SQNativeClosure) + (noutervalues*sizeof(SQObjectPtr)))

struct SQNativeClosure : public CHAINABLE_OBJ
{
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
private:
     SQNativeClosure(SQSharedState *ss,SQFUNCTION func, SQInteger helper = 0){_function=func;_funchelper=helper;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL;}
public:
    static  SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func,SQInteger nouters, SQInteger helper = 0)
#else
private:
     SQNativeClosure(SQFUNCTION func, SQInteger helper = 0){_function=func;INIT_CHAIN();_funchelper=helper;ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL;}
public:
    static  SQNativeClosure *Create(SQFUNCTION func,SQInteger nouters, SQInteger helper = 0)
#endif
    {
        SQInteger size = _CALC_NATVIVECLOSURE_SIZE(nouters);
        SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(size);
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
        new (nc) SQNativeClosure(ss,func, helper);
#else
        new (nc) SQNativeClosure(func, helper);
#endif
        nc->_outervalues = (SQObjectPtr *)(nc + 1);
        nc->_noutervalues = nouters;
        _CONSTRUCT_VECTOR(SQObjectPtr,nc->_noutervalues,nc->_outervalues);
        return nc;
    }
    SQNativeClosure  *Clone()
    {
#ifndef SQUIRREL_SINGLE_SHAREDSTATE
        SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function,_noutervalues, _funchelper);
#else
        SQNativeClosure * ret = SQNativeClosure::Create(_function,_noutervalues, _funchelper);
#endif
        ret->_env = _env;
        if(ret->_env) __ObjAddRef(ret->_env);
        ret->_name = _name;
        _COPY_VECTOR(ret->_outervalues,_outervalues,_noutervalues);
        ret->_typecheck.copy(_typecheck);
        ret->_nparamscheck = _nparamscheck;
        return ret;
    }
     ~SQNativeClosure()
    {
        __ObjRelease(_env);
        REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
    }
    void  Release(){
        SQInteger size = _CALC_NATVIVECLOSURE_SIZE(_noutervalues);
        _DESTRUCT_VECTOR(SQObjectPtr,_noutervalues,_outervalues);
        this->~SQNativeClosure();
        sq_free(this,size);
    }

#ifndef NO_GARBAGE_COLLECTOR
    void Mark(SQCollectable **chain) ;
    void  Finalize() { _NULL_SQOBJECT_VECTOR(_outervalues,_noutervalues); }
    SQObjectType  GetType() {return OT_NATIVECLOSURE;}
#endif
    SQInteger _nparamscheck;
    SQIntVec _typecheck;
    SQObjectPtr *_outervalues;
    SQUnsignedInteger _noutervalues;
    SQWeakRef *_env;
    union {
	    SQFUNCTION _function;
	    SQFUNCHELPED _funchelped;
    };
    // added to facilitate binding system
    SQUnsignedInteger _funchelper;
    SQObjectPtr _name;
};

#endif //_SQCLOSURE_H_
