#include <QString>
#include <QHash>
#include "php_qt.h"
#include "MethodCall.h"
#include "MethodReturnValue.h"
#include "phpqt_internals.h"
#include "marshall_types.h"
#include "context.h"

MethodCall::MethodCall(Smoke *smoke, const Smoke::Index method, zval* target, zval ***sp, zval *&retval) :
    MethodCallBase(smoke,method,*sp),
    _target(target),
    _current_object(0),
    _retval(retval)
    {
    if( target == 0 )
    {
        _target = (zval*) emalloc( sizeof(zval) );
        _retval = (zval*) emalloc( sizeof(zval) );
        Context::setStaticZVal( _target );
    }

    if (PHPQt::SmokePHPObjectExists(_target))
    {
        smokephp_object *o = PHPQt::getSmokePHPObjectFromZval(_target);
        if (o && o->ptr())
        {
            _current_object = o->mPtr();
            _current_object_class = o->classId();
        }
    }

    _args = _smoke->argumentList + _smoke->methods[_method].args;
    _items = _smoke->methods[_method].numArgs;
    // TODO memory test
    _stack = new Smoke::StackItem[_items + 1];
    }

MethodCall::~MethodCall()
{
    delete[] _stack;
}

Marshall::Action
MethodCall::action()
{
	return Marshall::FromZVAL;
}

zval*
MethodCall::var()
{
	if (_cur < 0) return _retval;
	return MethodCallBase::_sp[_cur];
}

void
MethodCall::callMethod() {
	if(_called) return;
	_called = true;

	QByteArray className(_smoke->className(method().classId));

	if (! className.endsWith(_smoke->methodNames[method().name])
		&& Z_TYPE_P(_target) == IS_NULL
		&& !(method().flags & Smoke::mf_static) )
	{
		php_error(E_ERROR, "Instance is not initialized, cannot call %s",
					_smoke->methodNames[method().name]);
	}

	if (Z_TYPE_P(_target) == IS_NULL && !(method().flags & Smoke::mf_static)) {
		php_error(E_ERROR, "%s is not a class method\n", _smoke->methodNames[method().name]);
	}

	const Smoke::ClassFn fn = _smoke->classes[method().classId].classFn;

	void *ptr = 0;
	if( _current_object != 0 )
		ptr = _smoke->cast(_current_object, _current_object_class, method().classId);

	_items = -1;

	(*fn)(method().method, ptr, _stack);
	// TODO return_value_used
	MethodReturnValue r(_smoke, _method, _stack, _retval);
}


const int
MethodCall::items()
{
	return _items;
}

bool
MethodCall::cleanup()
{
	return true;
}
/*
const char *
MethodCall::classname()
{
	return qstrcmp(MethodCallBase::classname(), "QGlobalSpace") == 0 ? "" : MethodCallBase::classname();
}
*/
