/***************************************************************************
 * rubyscript.h
 * This file is part of the KDE project
 * copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * You should have received a copy of the GNU Library General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 ***************************************************************************/

#include "rubyscript.h"
#include "rubyvariant.h"

#define HAVE_STRLCAT_PROTO 1
#define HAVE_STRLCPY_PROTO 1
#include "config.h"

#include <ruby.h>

#ifndef HAVE_RUBY_1_9
#include <env.h>
#include <rubysig.h>
#include <node.h>
#else // HAVE_RUBY_1_9
#ifndef HAVE_RUBY_3
#include <ruby/backward/rubysig.h>
#endif // HAVE_RUBY_3
#endif // HAVE_RUBY_1_9

#include <main/scriptcontainer.h>

#include "rubyconfig.h"
#include "rubyextension.h"
#include "rubyinterpreter.h"

namespace Kross {

namespace Ruby {

namespace Internals {
    namespace Script {
        static VALUE method_added(VALUE self, VALUE unit)
        {
            rb_funcall(self, rb_intern("module_function"), unit);
            return self;
        }
    };
};

class RubyScriptPrivate {
    friend class RubyScript;

    RubyScriptPrivate() : m_script(0), m_hasBeenCompiled(false)
    {
        if(RubyScriptPrivate::s_krossScript == 0)
        {
            RubyScriptPrivate::s_krossScript = rb_define_class_under(RubyInterpreter::krossModule(), "Script", rb_cModule);
            rb_define_method(RubyScriptPrivate::s_krossScript, "method_added", (VALUE (*)(...))Internals::Script::method_added, 1);
        }
    }
        VALUE m_script;
        static VALUE s_krossScript;
        bool m_hasBeenCompiled;

    /// A list of functionnames.
    TQStringList m_functions;

    /// A list of classnames.
    TQStringList m_classes;
};

VALUE RubyScriptPrivate::s_krossScript = 0;

RubyScript::RubyScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer)
    : Kross::Api::Script(interpreter, scriptcontainer), d(new RubyScriptPrivate())
{
}


RubyScript::~RubyScript()
{
}

void RubyScript::compile()
{
#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("RubyScript::compile()");
#endif
    int critical;

#ifdef HAVE_RUBY_1_9
    // FIXME
    int ruby_nerrs = 0;
    int ruby_errinfo = Qnil;
#else // HAVE_RUBY_1_9
    ruby_nerrs = 0;
    ruby_errinfo = Qnil;
#endif // HAVE_RUBY_1_9
    VALUE src = RubyExtension::toVALUE( m_scriptcontainer->getCode() );
    StringValue(src);
    VALUE name = RubyExtension::toVALUE( m_scriptcontainer->getName() );

#ifdef HAVE_RUBY_1_9
    // FIXME
#else // HAVE_RUBY_1_9
    critical = rb_thread_critical;
    rb_thread_critical = Qtrue;
    ruby_in_eval++;
#endif // HAVE_RUBY_1_9
    rb_funcall(d->m_script, rb_intern("module_eval"), 2, src, name);
#ifdef HAVE_RUBY_1_9
    // FIXME
#else // HAVE_RUBY_1_9
    ruby_in_eval--;
    rb_thread_critical = critical;
#endif // HAVE_RUBY_1_9

    if (ruby_nerrs != 0)
    {
#ifdef KROSS_RUBY_SCRIPT_DEBUG
        krossdebug("Compilation has failed");
#endif
        VALUE errorstring = rb_obj_as_string(ruby_errinfo);
        setException( new Kross::Api::Exception(TQString("Failed to compile ruby code: %1").arg(STR2CSTR( errorstring )), 0) ); // TODO: get the error
    } else {
        d->m_hasBeenCompiled = true;
    }
#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("Compilation was successfull");
#endif
}

const TQStringList& RubyScript::getFunctionNames()
{
#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("RubyScript::getFunctionNames()");
#endif
    if(not d->m_hasBeenCompiled )
    {
        compile();
    }
    return d->m_functions;
}

Kross::Api::Object::Ptr RubyScript::execute()
{
#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("RubyScript::execute()");
#endif
    // TODO: catch ruby exception
    compile();
#if 0
    if (result != 0)
    {
#ifdef KROSS_RUBY_SCRIPT_DEBUG
        krossdebug("Execution has failed");
#endif
        if( TYPE( ruby_errinfo )  == T_DATA && RubyExtension::isOfExceptionType( ruby_errinfo ) )
        {
#ifdef KROSS_RUBY_SCRIPT_DEBUG
            krossdebug("Kross exception");
#endif
            setException( RubyExtension::convertToException( ruby_errinfo ) );
        } else {
            setException( new Kross::Api::Exception(TQString("Failed to execute ruby code: %1").arg(STR2CSTR( rb_obj_as_string(ruby_errinfo) )), 0) ); // TODO: get the error
        }
    }
#endif // 0

#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("Execution is finished");
#endif
    return 0;
}

Kross::Api::Object::Ptr RubyScript::callFunction(const TQString& name, Kross::Api::List::Ptr args)
{
    Q_UNUSED(name)
    Q_UNUSED(args)
#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("RubyScript::callFunction()");
#endif
    if(not d->m_hasBeenCompiled )
    {
        compile();
    }

    Q_ASSERT(d->m_hasBeenCompiled );

#ifdef HAVE_RUBY_1_9
    // FIXME
    int ruby_errinfo = Qnil;
#endif // HAVE_RUBY_1_9

#if 0
    // FIXME
    // The original code never really did anything as far as I can tell!
    TQVariant result;
    int r = ruby_exec();
    if (r != 0) {
        #ifdef KROSS_RUBY_SCRIPT_DEBUG
            krossdebug("RubyScript::callFunction failed");
        #endif
        VALUE errorstring = rb_obj_as_string(ruby_errinfo);
        setException( new Kross::Api::Exception(TQString("Failed to call function \"%1\": %2").arg(name).arg(STR2CSTR( errorstring )), 0) ); // TODO: get the error
    }
    else {
        VALUE self = rb_eval_string("self");
        //krossdebug(TQString("RubyScript::callFunction() ===> %1").arg(STR2CSTR(rb_inspect(self))));

        const int rnargs = args.size();
        VALUE *rargs = new VALUE[rnargs];
        for(int i = 0; i < rnargs; ++i) {
            rargs[i] = RubyType<TQVariant>::toVALUE( args[i] );
        }

        //VALUE r = rb_eval_string("myFunc()");
        VALUE v = rb_funcall2(self, rb_intern(name.toLatin1()), rnargs, rargs);
        result = RubyType<TQVariant>::toVariant(v);
        delete[] rargs;
    }
#endif

    return 0;
}

const TQStringList& RubyScript::getClassNames()
{
#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("RubyScript::getClassNames()");
#endif
    if(not d->m_hasBeenCompiled )
    {
        compile();
    }
    return d->m_classes;
}

Kross::Api::Object::Ptr RubyScript::classInstance(const TQString& name)
{
    Q_UNUSED(name)
#ifdef KROSS_RUBY_SCRIPT_DEBUG
    krossdebug("RubyScript::classInstance()");
#endif
    if(not d->m_hasBeenCompiled )
    {
        compile();
    }

    Q_ASSERT(d->m_hasBeenCompiled );

    // FIXME
    // The original code never really did anything from what I can tell!

    return 0;
}


}

}
