/*
 * DuneApp.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 <errno.h>
#ifdef _WIN32
# include <direct.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#endif

#include <ctype.h>
#include <time.h>

#include "stdafx.h"
#include "swt.h"

#include "parser.h"
#include "DuneApp.h"
#include "Scene.h"
#include "Path.h"
#include "MainWindow.h"
#include "URL.h"
#include "EulerAngles.h"
#include "x3dtranslators.h"
#include "resource.h"
#include "TexteditSettingsDialog.h"
#include "xerrorhandler.h"

#define DEFAULT_TESSELLATION 32

DuneApp *TheApp = NULL;
extern void setLanguage(char *lang);

void returntracker(void)
   {
   TheApp->ReturnTracker();
   }

#define MAX_RECENT_FILES 4

DuneApp::DuneApp() : PreferencesApp(), EcmaScriptApp(), StereoViewApp(), 
                     InputDeviceApp()                     
{
    TheApp = this;
    swSetDefaultIcon(IDI_DUNE_ICON);
    _mainWnd = NULL;

    initPreferences();

    int n = GetIntPreference("RecentFiles", 0);
    for (int i = 0; i < n; i++) {
        char buf[1024];
        const char *filename;
        mysnprintf(buf, 1024, "RecentFile%d", i);
        filename = TheApp->GetPreference(buf, "");
        _recentFiles.append(filename);
    }
    _clipboardNode = NULL;
    swSetCleanup(returntracker);
    init_handlers();
    _currentWindow = NULL;
    _keyProtoFile = "ProtoFile";
    _keyProtoCategory = "ProtoCategory";
    _is4Kids = false;
    _is4Catt = false;
    _x3dv = false;
    _dontCareFocus = false;
    _numberLoadedInlines = 0;
    _coverMode = false;
    _kambiMode = false;
    _importURL = NULL;
    _fileDialogDir = NULL;
    _createAtZero[0] = false;
    _createAtZero[1] = false;
    _createAtZero[2] = false;
    _blackAndWhiteIcons = false;
    _glErrorCount = 0;
    _vrml1Error = false;
    _timeOut = -1;
    _timeOutSet = true;
    _lastInteraction = -1;
    _defaultTessellation = DEFAULT_TESSELLATION;
    if (_defaultTessellation <= 8)
        _defaultTessellation = 8;
    setLanguage(TheApp->GetStartLanguage()); 
    char *variant = TheApp->GetStartVariant();
    if (variant) {
        if (strcmp(variant, "4kids") == 0)
            TheApp->set4Kids();
        if (strcmp(variant, "4catt") == 0)
            TheApp->set4Catt();
        if (strcmp(variant, "cover") == 0)
            TheApp->setCoverMode();
        if (strcmp(variant, "kambi") == 0)
            TheApp->setKambiMode();
    }
    _selectionLinenumberFlag = false;
    _renderFaster = true;
}

void DuneApp::initPreferences(void)
{
    _browser = swBrowserInit(getPrefs());
    _upload = swUploadInit(getPrefs());
    _helpBrowser = swHelpBrowserInit(getPrefs());
    _textedit = swTextEditInit(getPrefs());
    StereoViewLoadPreferences();
    EcmaScriptLoadPreferences();
    InputLoadPreferences();
    OutputLoadPreferences();
    RouteViewLoadPreferences();
    StartWithLoadPreferences();
}

void 
DuneApp::setDefaults(void)
{
    swPreferencesDefault();
    initPreferences();
    PreferencesDefaults();
    InputSetDefaults();
    OutputSetDefaults();
    RouteViewSetDefaults();
    StartWithSetDefaults();
    StereoViewSetDefaults();
}



void
DuneApp::SaveRecentFileList()
{
    int i, n = _recentFiles.size();

    SetIntPreference("RecentFiles", n);
    for (i = 0; i < n; i++) {
        char buf[1024];
        mysnprintf(buf, 1024, "RecentFile%d", i);
        SetPreference(buf, _recentFiles[i]);
    }
}

// there may be only one message at a time

void 
DuneApp::PrintMessageWindows(const char *text)
{
    bool written = false;
    List<MainWindow *>::Iterator *i;
    for (i = _windows.first(); i; i = i->next()) {
         written = true;
         i->item()->setStatusText(text);
    }
    if (written == false)
        swDebugf("%s\n", text);
}


void 
DuneApp::PrintMessageWindowsId(int id)
{
    static char text[256];
    swLoadString(id, text, 255);
    PrintMessageWindows(text);
}

void 
DuneApp::PrintMessageWindowsInt(int id, int integer)
{
    char idText[256];
    static char text[512];
    swLoadString(id, idText, 255);
    mysnprintf(text, 511, idText, integer);
    PrintMessageWindows(text);

}

void 
DuneApp::PrintMessageWindowsFloat(int id, float f)
{
    char idText[256];
    static char text[512];
    swLoadString(id, idText, 255);
    mysnprintf(text, 511, idText, f);
    PrintMessageWindows(text);
}

void
DuneApp::PrintMessageWindowsString(int id, const char *string)
{
    char idText[256];
    static char text[512];
    swLoadString(id, idText, 255);
    mysnprintf(text, 511, idText, string);
    PrintMessageWindows(text);
}


void 
DuneApp::PrintMessageWindowsVertex(int id, const char *fieldName, int vertex)
{
    if (is4Kids())
        return;
    static char text[256];
    static char idText[256];
    swLoadString(id, idText, 255);
    mysnprintf(text, 255, idText, fieldName, vertex);
    PrintMessageWindows(text);
}

// there may be only one MessageBox at a time

char *
DuneApp::loadPrompt(int prompt)
{
     static char string[256];
     string[0] = 0;
     if (prompt == SW_MB_ERROR)
         swLoadString(IDS_DUNE_ERROR, string, 255);
     else if (prompt == SW_MB_WARNING)
         swLoadString(IDS_DUNE_WARNING, string, 255);
     else 
         swLoadString(IDS_DUNE, string, 255);
     return string;
}

void                
DuneApp::MessageBox(const char *text, int prompt)
{
     if (_windows.size() == 0)
         swDebugf("%s\n", text);
     else
         swMessageBox(_mainWnd, text, loadPrompt(prompt), SW_MB_OK, 
                      prompt == -1 ? SW_MB_ERROR : prompt);
}

void
DuneApp::MessageBoxId(int id, int prompt)
{
     char string[256];
     swLoadString(id, string, 255);
     MessageBox(string, prompt);
}

void
DuneApp::MessageBox(int id, const char *str, int prompt)
{
    static char text[512];

    char idText[256];
    swLoadString(id, idText, 255);
    mysnprintf(text, 511, idText, str);
    MessageBox(text, prompt);
}

void
DuneApp::MessageBox(int id, const char *str1, const char *str2, int prompt)
{
    static char text[1024];

    char idText[256];
    swLoadString(id, idText, 255);
    mysnprintf(text, 1023, idText, str1, str2);
    MessageBox(text, prompt);
}

void
DuneApp::MessageBox(int id, int integer, int prompt)
{
    static char text[256];

    char idText[256];
    swLoadString(id, idText, 255);
    mysnprintf(text, 255, idText, integer);
    MessageBox(text, prompt);
}
 
void
DuneApp::MessageBox(int id, float f, int prompt)
{
    static char text[256];

    char idText[256];
    swLoadString(id, idText, 255);
    mysnprintf(text, 255, idText, f);
    MessageBox(text, prompt);
}

void
DuneApp::MessageBoxPerror(const char *object)
{
   char error[1024];
   mysnprintf(error, 1023,"%s: %s", object, strerror(swGetLastError()));
   MessageBox(error); 
}

void
DuneApp::MessageBoxPerror(int id, const char *object)
{
   MessageBox(id, object, strerror(swGetLastError())); 
}


void 
DuneApp::newMainWnd(SWND &mainWnd)
{
    int width = GetIntPreference("WindowWidth", 800);
    int height = GetIntPreference("WindowHeight", 600);
    // sanity check
    if ((width <= 0) || (height <= 0)) {
        width = 800;
        height = 600;
    }
    mainWnd = swCreateMainWindow("Dune", 0, 0, width, height);
}

void
DuneApp::OnFileNew(bool x3d)
{
#ifndef HAVE_OPEN_IN_NEW_WINDOW
    if (!TheApp->checkSaveOldWindow())
        return;
#endif

    forgetNameTranslation();

    Scene *scene = new Scene();
    if (x3d)
        scene->setX3dv();
    newMainWnd(_mainWnd);
    MainWindow *window = new MainWindow(scene, _mainWnd);
    _windows.append(window);

#ifndef HAVE_OPEN_IN_NEW_WINDOW
    TheApp->deleteOldWindow();
#endif
}

void
DuneApp::OnFileNewWindow()
{
    Scene *scene = new Scene();
    if (_x3dv)
        scene->setX3dv();
    newMainWnd(_mainWnd);
    MainWindow *window = new MainWindow(scene, _mainWnd);
    _windows.append(window);
}

/* 
 * temporary files are written into the current directory 
 * temporary files are written into a list and deleted at program end
 */

char *
DuneApp::SaveTempFile(Scene *scene, const char *name, int writeFlags)
{
    static char path[1024];

    mystrncpy_secure(path,scene->getPath(),1024);
    int i=0;
    // produce a absolute path (remote netscape commands need one)
#ifdef _WIN32
    const char *dirsign="\\";
    if (path[1] != ':') 
        if (getcwd(path, 1023)!=NULL) {
#else
    const char *dirsign="/";
    if (path[0] != '/')
        if (getcwd(path, 1023)!=NULL) {
#endif
          mystrncpy_secure(path+strlen(path),dirsign,1024-strlen(path));
          mystrncpy_secure(path+strlen(path),scene->getPath(),
                           1024-strlen(path));
       }
    // strip filename
    for (i=strlen(path);(i>=0) && (path[i]!=dirsign[0]);i--);
    i++;
    swGetTempFile(path+i, name, ".wrl", 1024-i);
    int f = open(path, O_WRONLY | O_CREAT,00666);
  
    if (f == -1) {
        swGetTempPath(path, name, ".wrl", 1024);
        f = open(path, O_WRONLY | O_CREAT,00666);
        if (f == -1) {
            char errorstring1[256];
            swLoadString(IDS_SAVE_PREVIEW_ERROR1, errorstring1, 255);
            char errorstring2[256];
            swLoadString(IDS_SAVE_PREVIEW_ERROR2, errorstring2, 255);
            char msg[1024];
            mysnprintf(msg, 1023, errorstring1, path, strerror(errno), 
                       errorstring2);
            TheApp->MessageBox(msg);
            return (char *)"";
        }
    } 
    URL fileURL;
    fileURL.FromPath(path);
    bool writeError = false;
    if (scene->write(f, fileURL, writeFlags | TEMP_SAVE) != 0)
        writeError = true;
    else if (swTruncateClose(f))
        writeError = true;
    if (writeError)
        TheApp->MessageBoxPerror(path);
    AddToFilesToDelete(path);
    if (writeError)
        return (char *)"";    
    return path;
}

void DuneApp::OnFilePreview(Scene* scene)
{
#ifndef _WIN32
    if (!swBrowserGetUseFork(TheApp->GetBrowser()))
        swIconifyWindow(_mainWnd);
#endif
#ifdef HAVE_AFLOCK
    stopTrackers();
#endif
    char *temppath = SaveTempFile(scene,".dune_preview", 
                                  swBrowserGetVrmlLevel(TheApp->GetBrowser()));
    if (strlen(temppath) != 0)
        swBrowserPreview(GetBrowser(), temppath, _mainWnd);
#ifdef HAVE_AFLOCK
    restartTrackers();
#endif
#ifndef _WIN32
    if (!swBrowserGetUseFork(TheApp->GetBrowser())) {
        swDeIconifyWindow(_mainWnd);
        swInvalidateWindow(_mainWnd);
    }
#endif
}

void
DuneApp::OnFileUpload(Scene* scene)
{
    char *temppath = SaveTempFile(scene,".dune_upload",     
                                  swBrowserGetVrmlLevel(TheApp->GetBrowser()));
    char *htmlpath = (char *)"";
    if (strlen(temppath) != 0) {
        htmlpath = swUpload(_upload, temppath, _helpBrowser, _mainWnd);
    }
    if (strlen(htmlpath) != 0)
        AddToFilesToDelete(htmlpath);    
}


#ifndef HAVE_OPEN_IN_NEW_WINDOW

// save VRML files (with .wrl or .txt extension) into a list of temporaery 
// files and hide the windows (to call a editor next)
// the file of the currentWindow is first stored in the list

bool
DuneApp::saveTempFiles(MainWindow *currentWindow, int useExtensionTxt)
{
   _tempFiles.Init();
   int count = 0;
   List<MainWindow *>::Iterator *wIter;
   for (wIter = _windows.first(); wIter != NULL; wIter  = wIter ->next()) {
       Scene *scene = wIter->item()->GetScene();
       URL oldURL = scene->getURL();
       const char* oldPath = mystrdup(scene->getPath());

       char *filename = (char *)malloc(1024);
       mysnprintf(filename,1024,"%s_%d",".dune_textedit",count++);
       char *savefile = (char *)malloc(1024);
       if (!useExtensionTxt)
           if (scene->isX3d())
               swGetTempFile(savefile, filename, ".x3dv", 1024);
           else
               swGetTempFile(savefile, filename, ".wrl", 1024);
       else
           swGetTempFile(savefile, filename, ".txt", 1024);

       // is file writable ?
       int f = open(savefile, O_WRONLY | O_CREAT,00666);
       if (f == -1) {
           if (!useExtensionTxt)    
               swGetTempPath(savefile,filename,".wrl",1024);
           else
               swGetTempPath(savefile,filename,".txt",1024);
           f = open(savefile, O_WRONLY | O_CREAT,00666);
           if (f == -1) {
               char errorstring1[256];
               swLoadString(IDS_SAVE_EDIT_ERROR1, errorstring1, 255);
               char errorstring2[256];
               swLoadString(IDS_SAVE_EDIT_ERROR2, errorstring2, 255);
               char msg[1024];
               mysnprintf(msg, 1023, errorstring1, savefile, strerror(errno),
                          errorstring2);
               TheApp->MessageBox(msg);
               return false;
           }
       }
       AddToFilesToDelete(savefile);
       close(f);
       free(filename);

       URL url;
       url.FromPath(savefile);
       if (wIter->item() == currentWindow)
           initSelectionLinenumber();
       int writeFlags = TEMP_SAVE;
       if (scene->isX3d())
           writeFlags |= X3DV;
       if (wIter->item()->SaveFile(savefile, url, writeFlags)) {
           TheApp->removeClipboardNode(getClipboardNode());
           if ((currentWindow != NULL) && (wIter->item() == currentWindow))
               _tempFiles.insert(new FileBackup(savefile, oldURL, oldPath));
           else
               _tempFiles.append(new FileBackup(savefile, oldURL, oldPath));
           // hide and remove window
           swUpdate();
           swHideWindow(wIter->item()->getParentWindow());
       } else {
            // save failed, need to restore...
            return false;
       }  
    }
    // deleting from a shrinking List is not trivial...
    wIter = _windows.first(); 
    while (wIter != NULL) {
        MainWindow * windowToDelete = wIter->item();
        wIter  = wIter ->next();
        _windows.remove(_windows.find(windowToDelete));
        delete windowToDelete;
    }
    return true;
}

static bool checkLinenumberOption(const char *linenumberOption)
{
    bool hasLinenumberOption = (linenumberOption[0] != 0);

    // check if linenumber option only contain blanks
    if (hasLinenumberOption) {
        hasLinenumberOption = false;
        for (int i = 0; i < strlen(linenumberOption); i++)
            if (linenumberOption[i] != ' ')
                hasLinenumberOption = true;
    }
    return hasLinenumberOption;
}


void
DuneApp::restoreTempFiles(void)
{
    List<FileBackup *>::Iterator *fIter = _tempFiles.first();       
    for (int i = 0; i < _tempFiles.size(); i++) {
        Scene* scene = new Scene();
        forgetNameTranslation();
        bool error = false;
        do {
            newMainWnd(_mainWnd);
            if (ImportFile(fIter->item()->_backupFile, scene)) {
                scene->setExtraModifiedFlag();
                scene->setURL(fIter->item()->_url);
                scene->setPath(fIter->item()->_path);
                MainWindow *newwindow = new MainWindow(scene, _mainWnd);
                _windows.append(newwindow);
                error = false;
            } else {
                error = true;
                checkAndRepairTextEditCommand();

                const char *textEditCommand;
                const char *textEditLinenumberOption;
                int textEditUseExtensionTxt;

                swTextEditGetSettings(_textedit, 
                                      &textEditCommand, 
                                      &textEditLinenumberOption,
                                      &textEditUseExtensionTxt);

                MyString command = mystrdup(textEditCommand);
                if (checkLinenumberOption(textEditLinenumberOption)) {
                    command += " ";
                    command += mystrdup(textEditLinenumberOption);
                    char number[1024];
                    mysnprintf(number, 1023, "%d", scene->getErrorLineNumber());
                    command += number;
                }
                command += " "; 
                command += fIter->item()->_backupFile; 
                swHideWindow(_mainWnd);
                swUpdate();
                delete scene;
                scene = new Scene();
                system((const char*)command);
            }
        } while (error);
        free(fIter->item()->_backupFile);
        free(fIter->item()->_path);
        fIter = fIter->next();
        scene->findBindableNodes();
    }
}

void
DuneApp::OnFileEdit(MainWindow *window, Scene* oldScene, char* filename)
{
    checkAndRepairTextEditCommand();

    const char *TextEditCommand;
    const char *TextEditLinenumberOption;
    int TextEditUseExtensionTxt;

    swTextEditGetSettings(_textedit, 
                          &TextEditCommand, &TextEditLinenumberOption,
                          &TextEditUseExtensionTxt);

    swDisableTimers();

    bool saveSuccess = saveTempFiles(window, TextEditUseExtensionTxt);

    MyString command = mystrdup(TextEditCommand);
 
    if (checkLinenumberOption(TextEditLinenumberOption)) {
        command += " ";
        command += mystrdup(TextEditLinenumberOption);
        char number[1024];
        mysnprintf(number, 1023, "%d", getSelectionLinenumber());
        command += number;
    } 
         
    SavePreferences();
  
    int i;
    if (filename == NULL) {
         List<FileBackup *>::Iterator *fIter = _tempFiles.first();       
        for (i = 0; i < _tempFiles.size(); i++) {
            command += " ";
            command += fIter->item()->_backupFile;
            fIter = fIter->next();
        }
    } else {
        command += " ";
        command += filename;
    }

    bool commandFailed = false;
    swHideWindow(_mainWnd);
    swUpdate();
    if (saveSuccess)
        if (system((const char*)command) != 0)
           commandFailed = true; 
    swEnableTimers();
    newMainWnd(_mainWnd);
    if (!saveSuccess) { 
        char errorstring[256];
        swLoadString(IDS_SAVE_FAILED, errorstring, 255);
        TheApp->MessageBoxPerror(errorstring);
    }
    if (commandFailed)
        TheApp->MessageBoxId(IDS_EDIT_COMMAND_ERROR);
    swDestroyWindow(_mainWnd);
    restoreTempFiles();
}
#endif

bool 
DuneApp::AddFile(char* openpath, Scene* scene)
{
    URL         url;
    url.FromPath(openpath);

    scene->setURL(url);
    scene->setPath(openpath);

    if (!TheApp->ImportFile(openpath, scene)) {
        delete scene;
        return false;
    }
    return true;
}

bool
DuneApp::ImportFile(const char *openpath, Scene* scene, bool protoLibrary,
                    Node *node, int field)
{
    FILE       *file;
    char  path1[1024];
    char  path2[1024];
    char* filepath;

    mystrncpy_secure(path1,openpath,1024); 
    filepath=swKillFileQuotes(path1);
    bool readXml = false;
#ifdef READ_XML
    if (!useInternalXmlParser())
        if (swIsXML(filepath)) {
            swGetTempPath(path2,"from_xml",".wrl",1024);
            readXml =true;
            if (!x3d2vrml(path2,(char*)filepath))
                return false;
        }
#endif
    if (!readXml)
        mystrncpy_secure(path2,filepath,1024);
    if (strcmp(filepath, "-") == 0)
        file = stdin;
    else if (!path2[0] || !(file = fopen(path2, "rb"))) {
       return false;
    }

    URL importURL;
    importURL.FromPath(openpath);
    setImportURL(importURL);

    scene->saveProtoStatus();

    int undoStackTopBeforeParse = scene->getUndoStackTop();    

    const char *errors = scene->parse(file, protoLibrary, node, field);
    fclose(file);

    importURL=scene->getURL();

    scene->setSelection(scene->getRoot());
    scene->UpdateViews(NULL, UPDATE_SELECTION);
    scene->UpdateViews(NULL, UPDATE_ALL, NULL);

    if (errors[0]) {
#ifdef _WIN32
        swDebugf("%s", errors);
#endif
        TheApp->MessageBox(errors);
        // delete so far successful imported nodes on errors
        while ((scene->getUndoStackTop() > undoStackTopBeforeParse) &&
               scene->canUndo()) 
            scene->undo(); 
        scene->restoreProtoStatus();
           

        return false;
    }
    return true;
}

bool
DuneApp::OpenFile(const char *openpath)
{
    URL url;
    url.FromPath(openpath);

    forgetNameTranslation();

    Scene *scene = new Scene();
    scene->setURL(url);
    scene->setPath(openpath);

    if (!ImportFile(openpath, scene)) {
        delete scene;
        return false;
    }

    scene->findBindableNodes();

    scene->setSelection(scene->getRoot());
    scene->UpdateViews(NULL, UPDATE_SELECTION);
    scene->UpdateViews(NULL, UPDATE_ALL);
    AddToRecentFiles(openpath);

    // create a new window for our new scene
    newMainWnd(_mainWnd);
    MainWindow *window = new MainWindow(scene, _mainWnd);
    _windows.append(window);

    return true;
}

void DuneApp::OnFileClose(MainWindow *window)
{
    if (!window->SaveModified()) {
        return;
    }

    _windows.remove(_windows.find(window));
// MacOSX X11 can freeze here. Bug in X11 shutdown code ?
#ifndef MACOSX
    delete window;
#endif
    if (_windows.size() == 0) {
        for (int j=0;j<_filesToDelete.size();j++)
            swRemoveFile(*_filesToDelete[j]);
        swUploadCleanupPasswd(_upload);
        Exit();
    }
}

void DuneApp::OnFileExit()
{
    List<MainWindow *>::Iterator *i;
    for (i = _windows.first(); i; i = i->next()) {
        if (!i->item()->SaveModified()) return;
    }
    for (i = _windows.first(); i; i = i->next()) {
        delete i->item();
    }
    for (int j=0;j<_filesToDelete.size();j++)
        swRemoveFile(*_filesToDelete[j]);
    swUploadCleanupPasswd(_upload);
    Exit();
}

void DuneApp::Exit()
{
    SaveRecentFileList();

    SavePreferences();
    swBrowserShutdown(_browser);
// do not delete _prefs, cause there may be a crash in swExit 8-(
//    swDeletePreferences(_prefs);
    setNormalExit();
    swExit();
}

void DuneApp::UpdateAllWindows()
{
    for (List<MainWindow *>::Iterator *i = _windows.first(); i; i = i->next()) {
        i->item()->GetScene()->UpdateViews(NULL, UPDATE_ALL);
    }
}

int
DuneApp::GetNumRecentFiles() const
{
    return _recentFiles.size();
}

const MyString &
DuneApp::GetRecentFile(int index) const
{
    return _recentFiles[index];
}

void
DuneApp::AddToRecentFiles(const MyString &filename)
{
    int i = _recentFiles.find(filename);

    if (i >= 0) {
        _recentFiles.remove(i);
    }
    int n = MIN(_recentFiles.size(), MAX_RECENT_FILES-1);
    for (i = n; i > 0; i--) {
        _recentFiles[i] = _recentFiles[i-1];
    }
    _recentFiles[0] = filename;
}

void
DuneApp::AddToFilesToDelete(char* string)
{
    // test if string is already in list
    for (int i=0;i<_filesToDelete.size();i++)
       if (strcmp(string,(char*) _filesToDelete[i])==0)
          return;
    _filesToDelete.append(new MyString(string));
}

void
DuneApp::initSelectionLinenumber(void) 
{
    _selectionLinenumberFlag = false;
    _selectionLinenumber = 1;
}

void
DuneApp::checkSelectionLinenumberCounting(Scene* scene, Node* node)
{
    if (scene->getSelection()->getNode() == node)
        _selectionLinenumberFlag = true;
}

void
DuneApp::incSelectionLinenumber(int increment)
{
    if (!_selectionLinenumberFlag)
        _selectionLinenumber += increment;
}

int
DuneApp::getSelectionLinenumber(void) 
{
    if (_selectionLinenumberFlag)
        return _selectionLinenumber;
    else 
        return 1;
}

void                
DuneApp::reOpenWindow(Scene* scene)
{
#ifndef HAVE_OPEN_IN_NEW_WINDOW
    newMainWnd(_mainWnd);
    MainWindow *window = new MainWindow(scene, _mainWnd);
    _windows.append(window);
    TheApp->deleteOldWindow();
#endif    
}

bool 
DuneApp::checkSaveOldWindow(void)
{
    //  give user a chance to save first
    List<MainWindow *>::Iterator *curWinItr = _windows.last();
    _currentWindow = curWinItr->item();
    if (curWinItr != NULL) {
        if (curWinItr->item() != NULL) 
            if (!curWinItr->item()->SaveModified()) 
                return false;
        SavePreferences();
    }
    return true;
}

void 
DuneApp::deleteOldWindow(void)
{
    // delete the old window
    List<MainWindow *>::Iterator *curWinItr = NULL;
    for (List<MainWindow *>::Iterator *WinItr = _windows.first(); WinItr; 
        WinItr = WinItr->next())
        if (WinItr->item() == _currentWindow) {
            curWinItr = WinItr;
            _currentWindow = NULL;
            break;
        }
    if (curWinItr != NULL) {
        if (curWinItr->item() != NULL)
            delete curWinItr->item();
        _windows.remove(curWinItr);
    }
}

bool
DuneApp::hasUpload(void) 
{ 
    return swHasUpload(_upload);
}

void
DuneApp::addToProtoLibrary(char* category, char* protoFile)
{
    // search for a free key
    for (int i=1; i<1000; i++) {
        char key[1024];
        mysnprintf(key, 1023, "%s%d", _keyProtoFile, i);
        const char *value = GetPreference(key, "");
        if (strlen(value) == 0) {
            SetPreference(key, protoFile);
            mysnprintf(key, 1023, "%s%d", _keyProtoCategory, i);
            SetPreference(key,category);
            break;
        }
    }
}

bool
DuneApp::readProtoLibrary(Scene* scene)
{
    for (int i=1; i<1000; i++) {
        char key[1024];
        mysnprintf(key, 1023, "%s%d", _keyProtoFile, i);
        const char *value = GetPreference(key, "");
        if (strlen(value) != 0) {
            if (!ImportFile(value, scene))
                return false;
        }
    }
    return true;
}

bool 
DuneApp::loadNewInline()
{
    if (_numberLoadedInlines > GetMaxInlinesToLoad())
        return false; 
    _numberLoadedInlines++;
    return true;
}

bool DuneApp::browseCommand(char *buf, int len, int ids_text) 
{
    buf[0] = 0;
    SWND wnd = mainWnd();
    char prompt[256];

    swLoadString(ids_text + swGetLang(), prompt, 255);
#ifdef _WIN32
    const char *choice = "executable (*.exe, *.com, *.bat)\0*.exe;*.com;*.bat\0All Files (*.*)\0*.*\0\0";
#else
    const char *choice = "executable (*)\0*;\0\0";
#endif
    return swOpenExecutableDialog(wnd, prompt, choice, buf, len);
}

bool DuneApp::checkCommand(const char *oldCommand, bool checkForFile)
{
    bool checkFile = checkForFile;
#ifdef _WIN32
    checkFile = true;
#endif

    if (strlen(oldCommand) == 0)
        return false;

    // isolate first command and check for \ or /
    char *command = new char[strlen(oldCommand) + 1];
    strcpy(command, oldCommand);
    bool hasPathSeperator = false;
    bool hasQuote = false;
    bool isInString = false;
    for (int i = 0; i < strlen(command); i++)
        if ((command[i] == '/') || (command[i] == '\\'))
            hasPathSeperator = true;
        else if (command[i] == '"') {
            isInString = !isInString;
            hasQuote = true;
        } else if ((command[i] == ' ') && !isInString) {
            command[i] = 0;
            break;
        }

    // return if there is no path seperator
    if (checkFile)
        if (!hasPathSeperator) {
            delete [] command;
            return false;
        }

    // return if there is a "
    if (hasQuote) {
        delete [] command;
        return false;
    }

    if (checkFile) {
        // return if first command is not a file
        struct stat fileStat;
        if ((stat(command, &fileStat) == 0) && S_ISREG(fileStat.st_mode)) {
            delete [] command;
            return true;
        } else {
            delete [] command;
            return true;
        }
    }
    delete [] command;
    return true;
}

void DuneApp::checkAndRepairTextEditCommand(void)
{
    const char *textEditCommand;
    const char *textEditLinenumberOption;
    int textEditUseExtensionTxt;

    while (1) {
        swTextEditGetSettings(TheApp->GetTextedit(), 
                              &textEditCommand, &textEditLinenumberOption,
                              &textEditUseExtensionTxt);
        if (checkCommand(textEditCommand, false))
            break;
        TheApp->MessageBoxId(IDS_TEXTEDIT_PROMPT);
        TexteditSettingsDialog dlg(_mainWnd);
        dlg.DoModal();
    }
}

void DuneApp::checkAndRepairImageEditCommand(void)
{
    const char *imageEditCommand;

    while (1) {
        imageEditCommand = swImageEditGetSettings(TheApp->GetTextedit());
        if (checkCommand(imageEditCommand))
            break;
        TheApp->MessageBoxId(IDS_IMAGEEDIT_PROMPT);
        TexteditSettingsDialog dlg(_mainWnd);
        dlg.DoModal();
    }
}

void DuneApp::checkAndRepairImageEdit4KidsCommand(void)
{
    const char *imageEditCommand;

    while (1) {
        imageEditCommand = swImageEdit4KidsGetSettings(TheApp->GetTextedit());
        if (checkCommand(imageEditCommand))
            break;
        TheApp->MessageBoxId(IDS_IMAGEEDIT4KIDS_PROMPT);
        TexteditSettingsDialog dlg(_mainWnd);
        dlg.DoModal();
    }
}

void DuneApp::checkAndRepairSoundEditCommand(void)
{
    const char *soundEditCommand;

    while (1) {
        soundEditCommand = swSoundEditGetSettings(TheApp->GetTextedit());
        if (checkCommand(soundEditCommand))
            break;
        TheApp->MessageBoxId(IDS_SOUNDEDIT_PROMPT);
        TexteditSettingsDialog dlg(_mainWnd);
        dlg.DoModal();
    }
}

void DuneApp::checkAndRepairMovieEditCommand(void)
{
    const char *movieEditCommand;

    while (1) {
        movieEditCommand = swMovieEditGetSettings(TheApp->GetTextedit());
        if (checkCommand(movieEditCommand))
            break;
        TheApp->MessageBoxId(IDS_MOVIEEDIT_PROMPT);
        TexteditSettingsDialog dlg(_mainWnd);
        dlg.DoModal();
    }
}

int
DuneApp::printRenderErrors(bool printOnOutOfMemory)
{
    GLenum error = glGetError();
    bool printError = (error != GL_NO_ERROR);
    if (printError)
        if ((!printOnOutOfMemory) && (error == GL_OUT_OF_MEMORY))
            printError = false;
    if (printError)
        printRenderErrors(error);
    return error;
}

void
DuneApp::printRenderErrors(GLenum error)
{
    if (error != GL_NO_ERROR) {
        if (_glErrorCount < TheApp->GetX11ErrorsLimit())
            printf("%s\n",(const char *)gluErrorString(error));
        _glErrorCount++;
    }
}

int                 
DuneApp::searchIllegalChar(char *id)
{
    int invalidCharPosition = -1;
    // see Annex A.3 of ISO-19776
    static const int illegalFirstChars[13] = 
       { 
       0x22, 0x23, 0x27, 0x2b, 0x2c, 0x2d, 0x2e, 
       0x5b, 0x5c, 0x5d, 
       0x7b, 0x7d, 0x7f
       };
    static const int illegalChars[11] = 
       { 
       0x22, 0x23, 0x27, 0x2c, 0x2e, 
       0x5b, 0x5c, 0x5d, 
       0x7b, 0x7d, 0x7f 
       }; 
    if (!isalpha(id[0]))
        if (isdigit(id[0]))
            invalidCharPosition = 0;
        else
            if ((id[0] > 0) && (id[0] <= 0x20))
                invalidCharPosition = 0;
            else
                for (int j = 0; j < sizeof(illegalFirstChars)/sizeof(int); j++)
                    if (id[0] == illegalFirstChars[j]) {
                       invalidCharPosition = 0;
                       break;
                    }
    if (invalidCharPosition == -1) 
        for (int i = 1; i < strlen(id); i++)
            if ((!isalnum(id[i])) && (!isdigit(id[i])))
                if ((id[i] > 0) && (id[i] <= 0x20))
                    invalidCharPosition = i;
                else
                    for (int j = 0; j < sizeof(illegalChars)/sizeof(int); j++)
                        if (id[i] == illegalChars[j]) {
                            invalidCharPosition = i;
                            break;
                        }
    return invalidCharPosition;
}


void                
DuneApp::interact(void)
{
    if (_timeOut > 0) {
        _lastInteraction = time(NULL);
        _timeOutSet = false;
    }
}

bool                
DuneApp::timeOut(void)
{
    if (_timeOutSet)
        return true;
    if ((time(NULL) - _lastInteraction) >= _timeOut) {
        _timeOutSet = true;
        // set selection to root of screengraph on timeout
        List<MainWindow *>::Iterator *wIter;
        for (wIter = _windows.first(); wIter != NULL; wIter  = wIter ->next()) {
            Scene *scene = wIter->item()->GetScene();
            scene->setSelection(scene->getRoot());
            scene->UpdateViews(NULL, UPDATE_SELECTION);    
        }
        return true;
    }
    return false;
}


FileBackup::FileBackup(char* backupFile, URL url, const char* path)
{
     _backupFile = backupFile;
     _url = url;
     _path = (char*) path;
}

