// -*- c-basic-offset: 2 -*-
/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
 *
 *  This library 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 library 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 library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 */
#include "kjs/wk_config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include <kjs/value.h>
#include <kjs/object.h>
#include "types.h"
#include <kjs/interpreter.h>
#include "kjs/JSGlobalObject.h"

#include "bal_object.h"
#include "bal_runtime.h"
#include "bal_class.h"
#include "bal_instance.h"

using namespace KJS;
using namespace KJS::Bindings;


class MyObject2 : public BalObject
{
public:
    MyObject2()
    {
        printf("myobject2\n");
        addMethod("log");
    }

    ~MyObject2()
    {
    }

    JSValue *invoke( const char *name, ExecState* exec, const List& args)
    {
        printf("invoke objet 2\n");
        if (!strcmp(name, "log") ) {
            JSType type = args.at(0)->type();
            if (args.size() == 1 && type == StringType)
            {
                logMessage (args.at(0)->toString(exec).ascii());
            }
        }
        return jsUndefined();
    }

    JSValue *getProperty( const char *name, ExecState* exec )
    {
        printf("get property objet 2\n");
        return jsUndefined();
    };
    void setProperty( const char *name, ExecState* exec, const JSValue *value) 
    {
        printf("set property objet 2\n");
    };
    void logMessage (const char *message)
    {
        printf ("%s\n", message);
    }
};



class MyObject : public BalObject
{
public:
    MyObject():doubleValue(5.5), intValue(454), stringValue( "toto" ), booleanValue( true ), myObject2(0)
    {
        printf("myobject\n");
        addMethod("logMessage");
        addMethod("setIntValue");
        addMethod("getIntValue");
        addMethod("getStringValue");

        addProperty("doubleValue");
        addProperty("intValue");
        addProperty("stringValue");
        addProperty("booleanValue");
        addProperty("nullValue");
        addProperty("undefinedValue");
        addProperty("myTest");
    }

    ~MyObject()
    {
        if( myObject2 != NULL )
            delete myObject2;
    }

    JSValue *invoke( const char *name, ExecState* exec, const List& args)
    { 
        if (!strcmp(name, "logMessage") ) {
            JSType type = args.at(0)->type();
            if (args.size() == 1 && type == StringType)
            {
                logMessage (args.at(0)->toString(exec).ascii());
            }
        }
        else if (!strcmp(name, "setIntValue") ) {
            JSType type = args.at(0)->type();
            if( type == NumberType )
            {
                intValue = (int)args.at(0)->toNumber(exec);
                return jsNumber(intValue);
            }
        }
        else if (!strcmp(name, "getIntValue") ) {
            return jsNumber(intValue);
        }
        else if (!strcmp(name, "getStringValue") ) {
            return jsString(stringValue);
        }
        return jsUndefined();
    }

    JSValue *getProperty( const char * name, ExecState* exec) 
    {
        if( !strcmp( name, "doubleValue" ) )
        {
            return jsNumber(doubleValue);
        }
        else if( !strcmp( name, "intValue" ) )
        {
            return jsNumber(intValue);
        }
        else if( !strcmp( name, "stringValue" ) )
        {
            return jsString(stringValue);
        }
        else if( !strcmp( name, "booleanValue" ) )
        {
            return jsBoolean(booleanValue);
        }
        else if( !strcmp( name, "nullValue" ) )
        {
            return jsNull();
        }
        else if( !strcmp( name, "undefinedValue" ) )
        {
            return jsUndefined();
        }
// FIXME
        else if( !strcmp( name, "myTest" ) )
        {
            if( myObject2 == NULL )
            {
                myObject2 = new MyObject2();
            }
            return Instance::createRuntimeObject(Instance::BalLanguage, myObject2, findRootObject(exec->dynamicInterpreter()));
        }
        return jsUndefined();
    };
    void setProperty( const char *name, ExecState* exec, const JSValue *value) 
    {
        if( !strcmp( name, "intValue" ) )
        {
            intValue = (int)value->toNumber(exec);
        }
    };
    void logMessage (const char *message)
    {
        printf ("%s\n", message);
    }

private:
    double doubleValue;
    int intValue;
    char *stringValue;
    bool booleanValue;
    MyObject2 *myObject2;
};

// --------------------------------------------------------

class Global : public JSGlobalObject {
public:
  virtual UString className() const { return "global"; }
};

#define BufferSize 200000
static char code[BufferSize];

const char *readJavaScriptFromFile (const char *file)
{
    FILE *f = fopen(file, "r");
    if (!f) {
        fprintf(stderr, "Error opening %s.\n", file);
        return 0;
    }

    int num = fread(code, 1, BufferSize, f);
    code[num] = '\0';
    if(num >= BufferSize)
        fprintf(stderr, "Warning: File may have been too long.\n");

    fclose(f);

    return code;
}

int main(int argc, char **argv)
{
    // expecting a filename
    if (argc < 2) {
        fprintf(stderr, "You have to specify at least one filename\n");
        return -1;
    }

    bool ret = true;
    {
        JSLock lock;

        // create interpreter w/ global object
        Global* global = new Global();
        RefPtr<Interpreter> interp = new Interpreter(global);
        ExecState *exec = interp->globalExec();

        MyObject *myObject = new MyObject();
        RefPtr<RootObject> root = RootObject::create(0, interp);
        global->put(exec, Identifier("myInterface"), Instance::createRuntimeObject(Instance::BalLanguage, (void *)myObject, root));

        for (int i = 1; i < argc; i++) {
            const char *code = readJavaScriptFromFile(argv[i]);

            if (code) {
                // run
                Completion comp(interp->evaluate("", 0, code));

                /*if (comp.complType() == Throw) {
                    JSValue *exVal = comp.value();
                    char *msg = exVal->toString(exec).ascii();
                    int lineno = -1;
                    if (exVal->type() == ObjectType) {
                        JSValue *lineVal = exVal->getObject()->get(exec, Identifier("line"));
                        if (lineVal->type() == NumberType)
                            lineno = int(lineVal->toNumber(exec));
                    }
                    if (lineno != -1)
                        fprintf(stderr,"Exception, line %d: %s\n",lineno,msg);
                    else
                        fprintf(stderr,"Exception: %s\n",msg);
                    ret = false;
                }
                else if (comp.complType() == ReturnValue) {
                    char *msg = comp.value()->toString(interp->globalExec()).ascii();
                    fprintf(stderr,"Return value: %s\n",msg);
                }*/
            }
        }

        delete myObject;

    } // end block, so that Interpreter and global get deleted

    return ret ? 0 : 3;
}
