/*
 * NodeScript.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include "stdafx.h"
#include <ctype.h>

#include "NodeScript.h"
#include "Scene.h"
#include "Proto.h"
#include "FieldValue.h"
#include "MFString.h"
#include "SFBool.h"
#include "ExposedField.h"
#include "Field.h"
#include "EventIn.h"
#include "EventOut.h"
#include "DuneApp.h"
#include "RouteCommand.h"
#include "UnRouteCommand.h"
#include "URL.h"

ProtoScript::ProtoScript(Scene *scene)
  : Proto(scene, "Script")
{
    url.set(
          addExposedField(MFSTRING, "url", new MFString(), 
                          FF_STATIC | FF_URL, NULL));
    directOutput.set(
          addField(SFBOOL, "directOutput", new SFBool(false), FF_STATIC, NULL));
    mustEvaluate.set(
          addField(SFBOOL, "mustEvaluate", new SFBool(false), FF_STATIC, NULL));
}

Node *
ProtoScript::create(Scene *scene)
{ 
    return new NodeScript(scene); 
}

NodeScript::NodeScript(Scene *scene)
  : DynamicFieldsNode(scene, new ProtoScript(scene))
{
    _wantInitialize = false;
    _wantEventsProcessed = false;
    _wantShutdown = false;
}

NodeScript::~NodeScript()
{
    delete _proto;
}
 
// since scripts can have dynamically-added fields, we need to update
// the node's fields after the script is defined.
// Same problem for the scriptinterfacebuilder (ScriptDialog) (add or delete)
//
// It is important, that only one item (field, eventIn or eventOut) can
// be added or deleted during this call of update.

void
NodeScript::update()
{
    updateDynamicFields();
}

// strstr like function, that do not care about whitespace characters

bool 
NodeScript::strwhitespacestr(const char* string, const char* what)
{
    char *whatPtr = mystrdup(what);
    int   whatLen = strlen(whatPtr);

    // split a whitespace seperated whatPtr into zero seperated substrings
    for (int i = 0; i < whatLen; i++)
        if (isspace(whatPtr[i]))
            whatPtr[i] = (char)0;

    const char *stringPtr = string;
    int         stringLen = strlen(stringPtr);
    char       *stringEnd = (char *)stringPtr + stringLen;

    while (stringPtr < stringEnd) {
        int whatStart = 0;
        // skip first whitespaces of "what"
        while ((whatStart < whatLen) && (whatPtr[whatStart]==0)) 
            whatStart++;
        // search for first "what" substring in rest of string
        if (stringPtr = strstr(stringPtr, whatPtr + whatStart)) {
            int stringStart = 0;
            stringPtr += strlen(whatPtr+whatStart);
            whatStart += strlen(whatPtr+whatStart);
            while (whatStart <= whatLen) {
                // find next "what" substring
                while ((whatStart < whatLen) && (whatPtr[whatStart]==0)) 
                    whatStart++;
                // "what" string complete ?
                if (whatStart >= whatLen) {
                    free(whatPtr);
                    return true;
                }
                // skip whitespaces in string
                while (isspace(stringPtr[stringStart])) 
                    stringStart++;
                // compare next substring
                if (stringncmp(stringPtr+stringStart, whatPtr+whatStart)==0) {
                    stringStart += strlen(whatPtr+whatStart);
                    whatStart   += strlen(whatPtr+whatStart);
                } else
                   break;
            }
        } else {
            break;
        }
    } 
    free(whatPtr);
    return false;
}

bool
NodeScript::hasInitialize(char *string)
{
    const char *urlString = NULL;
    if (string == NULL) {
        urlString = getEcmaScriptUrl();
        if (urlString == NULL)
            return false;
    } else
        urlString = string;
    MyString cmptext = " function initialize (";
    return strwhitespacestr(urlString, cmptext);
}

bool
NodeScript::hasShutdown(char *string)
{
    const char *urlString = NULL;
    if (string == NULL) {
        urlString =  getEcmaScriptUrl();
        if (urlString == NULL)
            return false;
    } else
        urlString = string;
    MyString cmptext = " function shutdown (";
    return strwhitespacestr(urlString, cmptext);
}

bool           
NodeScript::hasEventsProcessed(char* string)
{
    const char *urlString = NULL;
    if (string == NULL) {
        urlString =  getEcmaScriptUrl();
        if (urlString == NULL)
            return false;
    } else
        urlString = string;
    MyString cmptext = " function eventsProcessed (";
    return strwhitespacestr(urlString, cmptext);
}

const char *
NodeScript::getEcmaScriptUrl(void) 
{
    MFString *mfurl = (MFString *) url();
    bool hasUrl = true;
    if (mfurl == NULL)
        hasUrl = false;
    if (mfurl->getSize() == 0)
        hasUrl = false;
    if (hasUrl) {
        for (int i = 0; i < mfurl->getSize(); i++)
             if (isSortOfEcmascript(mfurl->getValue(i)))
                 return mfurl->getValue(i);
    } else 
        return NULL;
}

void
NodeScript::buildInterfaceData(void) 
{     
    DynamicFieldsNode::buildInterfaceData();
    if (hasInitialize() || _wantInitialize)
        _interface.append(new InterfaceData(EL_INITIALIZE, -1));
    if (hasEventsProcessed() || _wantEventsProcessed)
        _interface.append(new InterfaceData(EL_EVENTS_PROCESSED, -1));
    if (hasShutdown() || _wantShutdown)
        _interface.append(new InterfaceData(EL_SHUTDOWN, -1));
}
