/*
 * psupdaterengine.cpp
 *
 * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
 *
 *
 * 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 (version 2 of the License)
 * 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; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
#include <psconfig.h>

#include <iutil/stringarray.h>
#include <iutil/cfgmgr.h>
#include <csutil/event.h>
#include <iutil/eventq.h>
#include <iutil/objreg.h>
#include <iutil/plugin.h>
#include <iutil/vfs.h>
#include <ivideo/natwin.h>
#include <imap/loader.h>
#include <iutil/virtclk.h>
#include <iutil/cmdline.h>
#include <csutil/cmdhelp.h>
#include <ivaria/stdrep.h>
#include <csutil/debug.h>
#include <csutil/cmdline.h>
#include <ivideo/graph2d.h>
#include <ivideo/graph3d.h>
#include <csutil/scopedmutexlock.h>

#include "util/pscssetup.h"
#include "updaterglobals.h"

#include <iengine/engine.h>
#include "psupdaterengine.h"

#include "paws/pawsmanager.h"
#include "paws/pawsmainwidget.h"
#include "paws/pawstextbox.h"
#include "paws/pawsbutton.h"
#include "pawsupdaterwindow.h"
#include "pawsserverlist.h"
#include "pawsupdateroptions.h"

#include "registry.h"
#include "registrycreator.h"
#include "updater.h"
#include "updaterconfig.h"
#include "fileutil.h"

#ifdef CS_PLATFORM_WIN32
#include <windows.h>
#endif

using updater::Updater;
using updater::Registry;
using updater::ChangeList;
using updater::RegistryCreator;

psUpdaterEngine* psupdaterengine;

#define  CONFIGFILENAME       "/this/psupdater.cfg"

#ifdef APPNAME
#undef APPNAME
#endif
#define  APPNAME              "PlaneShift Updater"

#define SLEEP_TIME 100


/*********************************************************************/
CS_IMPLEMENT_APPLICATION

/*********************************************************************/
psUpdaterEngine::psUpdaterEngine (iObjectRegistry *objectreg, bool silent)
{ 
    object_reg  = objectreg;
    paws        = NULL;
    drawScreen  = true;
    silentMode  = silent;
    createMode  = false;
    canUpdate   = false;
    update      = NULL;

    // Create the mutex that will control access to data updated from the background thread (in non-silent mode)
    status_mutex = csMutex::Create();
}


/*********************************************************************/
psUpdaterEngine::~psUpdaterEngine ()
{            
    if (scfiEventHandler && queue)
        queue->RemoveListener (scfiEventHandler);

    delete paws;  

    update = NULL;

    vfs = NULL;
}


/*********************************************************************/
#define PSAPP    "planeshift.updater.application"

#define PS_QUERY_PLUGIN(myref,intf, str)                          \
myref = CS_QUERY_REGISTRY (object_reg, intf);                      \
if (!myref) {                                                     \
    csReport (object_reg, CS_REPORTER_SEVERITY_ERROR, PSAPP,      \
       "No " str " plugin!");\
    return false;\
}

bool psUpdaterEngine::Initialize ()
{
    // Initialize updater

    update = csPtr<Updater>(new Updater);

    if (update != NULL)
    {
        if (update->Initialize(object_reg))
        {
            canUpdate = true;
        }
        else
        {
            pawsUpdaterWindow* updWnd = (pawsUpdaterWindow*)paws->FindWidget("updater");
            pawsButton* btn = (pawsButton*)updWnd->FindWidget("UpdateButton");
            btn->SetColour(graphics2d->FindRGB(255,0,0));
            canUpdate = false;
        }
    }

    if (!silentMode)
    {
        graphics2d = graphics3d->GetDriver2D();

        graphics2d->AllowResize(false);

        iNativeWindow *nw = graphics2d->GetNativeWindow();
        if (nw)
            nw->SetTitle(APPNAME);

        // Create the PAWS window manager 
        paws = new PawsManager( object_reg ,"/planeshift/art/apps.zip");
        if ( !paws ) 
            return false;
        mainWidget = new pawsMainWidget();

        paws->SetMainWidget( mainWidget );

        RegisterFactories();

        // Load and assign a default button click sound for pawsbutton
        //paws->LoadSound("/this/art/music/gui/ccreate/next.wav","sound.standardButtonClick");

        psupdaterengine->LoadWidgets();

        pawsWidget* setup = paws->FindWidget("updater");
        setup->SetBackgroundAlpha(0);

        paws->GetMouse()->ChangeImage("Standard Mouse Pointer");

        
        // Register our event handler
        scfiEventHandler = csPtr<EventHandler> (new EventHandler (this));
	csEventID esub[] = {
	  csevPreProcess (object_reg),
	  csevProcess (object_reg),
	  csevPostProcess (object_reg),
	  csevFinalProcess (object_reg),
	  csevFrame (object_reg),
	  csevMouseEvent (object_reg),
	  csevKeyboardEvent (object_reg),
	  CS_EVENTLIST_END
	};
        queue->RegisterListener(scfiEventHandler, esub);

    //End silent mode skipper
    }
        
    // Inform debug that everything initialized succesfully
    csReport (object_reg, CS_REPORTER_SEVERITY_NOTIFY, PSAPP,
        "psUpdaterEngine initialized.");
    
    return true;
}


/*********************************************************************/
bool psUpdaterEngine::HandleEvent (iEvent &ev)
{    
    lastEvent = &ev;
    static bool drawFrame;

    if ( paws->HandleEvent( ev ) )
    {        
        return true;
    }
    
    // Scoped lock is automatically released
    csScopedMutexLock status_lock(status_mutex);
    if (ev.Name == csevProcess (object_reg))
        return true;
    else if (ev.Name == csevFinalProcess (object_reg))
    {

        FinishFrame ();
        return true;
    }
    else if (ev.Name == csevPostProcess (object_reg))
    {
       SetupFrame ();


        if (drawScreen)
        {
            FrameLimit();
            graphics3d->BeginDraw(engine->GetBeginDrawFlags() | CSDRAW_2DGRAPHICS );
            paws->Draw();
        }
        else
        {
            csSleep(150);
        }
    }
    else if (ev.Name == csevCanvasHidden (object_reg, graphics3d->GetDriver2D ()))
    {
        drawScreen = false;
    }
    else if (ev.Name == csevCanvasExposed (object_reg, graphics3d->GetDriver2D ()))
    {
        drawScreen = true;
    }
    else if (ev.Name == csevQuit (object_reg))
    {
	if(GetUpdater())
	    GetUpdater()->Die();
    }

    return false;
}


/*********************************************************************/

void psUpdaterEngine::FrameLimit()
{
    csTicks sleeptime;
    elapsed +=vc->GetElapsedTicks();

    // Define sleeptime to limit fps to around 20
    sleeptime = 50;

    // Here we sacrifice drawing time
    if(elapsed < sleeptime)
        csSleep(sleeptime - elapsed);

    elapsed = 0;
}


void psUpdaterEngine::QuitClient()
{
    csRef<iConfigManager> cfg (CS_QUERY_REGISTRY (object_reg, iConfigManager));
    if (cfg)
        cfg->Save();
    
    queue->GetEventOutlet()->Broadcast (csevQuit (object_reg));
}


/*********************************************************************/
void psUpdaterEngine::SetupFrame()
{

}

void psUpdaterEngine::FinishFrame()
{
    graphics3d->FinishDraw ();
    graphics3d->Print (NULL);
}


/*********************************************************************/
#define RegisterFactory(factoryclass)   \
    factory = new factoryclass();


void  psUpdaterEngine::RegisterFactories()
{
    pawsWidgetFactory* factory;

    RegisterFactory (pawsUpdaterWindowFactory);
    RegisterFactory (pawsServerListFactory);
    RegisterFactory (pawsUpdaterOptionsFactory);
}


void psUpdaterEngine::LoadWidgets()
{
        bool ok = true;

        if(!paws->LoadWidget( "data/updater/updaterwindow.xml" ))
        {
            printf("Widget data/updater/updaterwindow.xml failed to load!\n");
            ok = false;
        }
    
        if(!paws->LoadWidget( "data/updater/updateroptions.xml" ))
        {
            printf("Widget data/updater/updateroptions.xml failed to load!\n");
            ok = false;
        }
    
        if(!paws->LoadWidget( "data/updater/serverlist.xml" ))
        {
            printf("Widget data/updater/serverlist.xml failed to load!\n");
            ok = false;
        }
    
        if(!paws->LoadWidget( "data/gui/ok.xml" ))
        {
            printf("Widget data/gui/ok.xml failed to load!\n");
            ok = false;
        }

        if ( !ok )
        {
            printf("One or more widgets failed to load\n");
            PS_PAUSEEXIT(1);
        }
}

void psUpdaterEngine::OutToScreen(const char* format,...)
{
    csString string;

    va_list args;
    va_start (args, format);
    string.FormatV (format, args);
    va_end (args);  
    OutToScreen(-1,string);
}

void psUpdaterEngine::OutToScreen(int color,const char* format,...)
{
    csString string;

    va_list args;
    va_start (args, format);
    string.FormatV (format, args);
    va_end (args);

    // Scoped lock is automatically released at function exit
    csScopedMutexLock status_lock(status_mutex);

    if (silentMode)
    {
        printf("%s\n",string.GetData());
        return;
    }

    pawsUpdaterWindow* updWnd = (pawsUpdaterWindow*)PawsManager::GetSingleton().FindWidget("updater");
    pawsMessageTextBox* msgBox = (pawsMessageTextBox*)updWnd->FindWidget("list");
    msgBox->AddMessage(string,color);
}

bool psUpdaterEngine::LaunchProgram(const char* path,const char* in)
{
#ifdef CS_PLATFORM_WIN32
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    // Start the child process. 
    if( !CreateProcess( NULL,   // No module name (use command line). 
        LPSTR(path), // Command line. 
        NULL,             // Process handle not inheritable. 
        NULL,             // Thread handle not inheritable. 
        false,            // Set handle inheritance to FALSE. 
        CREATE_NEW_CONSOLE, // Detach the process from the parent, this will also remove the console
        NULL,             // Use parent's environment block. 
        LPSTR(in),        // Use the specified startup directory
        &si,              // Pointer to STARTUPINFO structure.
        &pi )             // Pointer to PROCESS_INFORMATION structure.
    )
    {
        printf( "CreateProcess failed (%d).\n", GetLastError() );
        return false;
    }
#else
    printf("Program launch N/A\n");
#endif

    return true;
}

int psUpdaterEngine::FindRGB(int r,int g, int b)
{
    if(silentMode)
        return 0;
    else
        return graphics2d->FindRGB(r,g,b);
}

bool psUpdaterEngine::InitializeCS()
{  

    if (!object_reg)
        PS_PAUSEEXIT(1);
  
    csDebuggingGraph::SetupGraph(object_reg);

    //Load VFS and set up the standard stuff
    if ( !csInitializer::SetupConfigManager(object_reg, 0))
    {
        csReport(object_reg, CS_REPORTER_SEVERITY_ERROR, "psclient",
               "csInitializer::SetupConfigManager failed!\n"
               "Is your CRYSTAL environment variable set?");
      PS_PAUSEEXIT(1);
    }

    vfs  = CS_QUERY_REGISTRY (object_reg, iVFS);
    configMgr = CS_QUERY_REGISTRY (object_reg, iConfigManager);

    iConfigFile* cfg = configMgr->AddDomain(CONFIGFILENAME,vfs,iConfigManager::ConfigPriorityApplication+1);
    configMgr->SetDynamicDomain(cfg);

    //Load 3D driver if we're in non-silent mode
    if (!silentMode)
    {
        if (!csInitializer::RequestPlugins(object_reg,
                        CS_REQUEST_ENGINE,
                        CS_REQUEST_PLUGIN("crystalspace.font.server.freetype2",iFontServer),
                        CS_REQUEST_OPENGL3D,
                        CS_REQUEST_PLUGIN("crystalspace.graphic.image.io.multiplexer",iImageIO),
                        CS_REQUEST_END))
        {
            csReport(object_reg, CS_REPORTER_SEVERITY_ERROR, 
               "psupdater", "Failed to initialize plugins!");
            return 0;
        }
    }

    csRef<iPluginManager> pluginMgr;
    PS_QUERY_PLUGIN(pluginMgr,iPluginManager,"iPluginManager");

    // Load plugins
    xmlSystem = CS_LOAD_PLUGIN(pluginMgr,"crystalspace.documentsystem.tinyxml",iDocumentSystem);
    if(!xmlSystem)
    {
            csReport(object_reg, CS_REPORTER_SEVERITY_ERROR, 
               "psupdater", "Failed to load the XML parser!");        
            return false;
    }

    // Check for commandline help.
    if (csCommandLineHelper::CheckHelp (object_reg))
    {
        csCommandLineHelper::Help (object_reg);
        PS_PAUSEEXIT(1);
    }

    //Open application (?)
    if (!csInitializer::OpenApplication (object_reg))
    {
        csReport (object_reg, CS_REPORTER_SEVERITY_ERROR, 
                "psclient",
                "csInitializer::OpenApplication failed!\n"
                "Is your CRYSTAL environment var set?");
        csInitializer::DestroyApplication (object_reg);
        PS_PAUSEEXIT(1);
    }

    // tweak reporter plugin to report everything...
    // is there a command line switch or something to do this which I've missed?
    csRef<iStandardReporterListener> reporter = 
        CS_QUERY_REGISTRY (object_reg, iStandardReporterListener);
    if (reporter) 
    {
        reporter->ShowMessageID(CS_REPORTER_SEVERITY_BUG, true);
        reporter->ShowMessageID(CS_REPORTER_SEVERITY_ERROR, true);
        reporter->ShowMessageID(CS_REPORTER_SEVERITY_NOTIFY, true);
        reporter->ShowMessageID(CS_REPORTER_SEVERITY_DEBUG, true);
        reporter->ShowMessageID(CS_REPORTER_SEVERITY_WARNING, true);
        reporter->SetMessageDestination(CS_REPORTER_SEVERITY_NOTIFY, true, false, false, false, true);
        reporter->SetMessageDestination(CS_REPORTER_SEVERITY_ERROR, true, false, false, false, true);
        reporter->SetMessageDestination(CS_REPORTER_SEVERITY_WARNING, true, false, false, false, true);
        reporter->SetMessageDestination(CS_REPORTER_SEVERITY_BUG, true, false, false, false, true);
        reporter->SetMessageDestination(CS_REPORTER_SEVERITY_DEBUG, true, false, false, false, true);
    }

    //mount some paths
    if ( !vfs->Mount ("/planeshift/", "$^") )
        return false;

    //set the plugins
    PS_QUERY_PLUGIN (queue,  iEventQueue,    "iEventQueue");
    if(!silentMode)
    {
        PS_QUERY_PLUGIN (engine, iEngine,               "iEngine");
        PS_QUERY_PLUGIN( graphics3d   , iGraphics3D,    "iGraphics3D" );
    }

    PS_QUERY_PLUGIN (cfgmgr, iConfigManager, "iConfigManager");
    PS_QUERY_PLUGIN (vc,     iVirtualClock,  "iVirtualClock");

    return true;
}


void psUpdaterEngine::CriticalError(const char* format,...)
{
    csString error;

    va_list args;
    va_start (args, format);
    error.FormatV (format, args);
    va_end (args);

    printf("CRITICAL ERROR: %s\n",error.GetData());

    if (!silentMode)
    {
#ifdef CS_PLATFORM_WIN32
        MessageBox(0,error.GetData(),"Critical Error",MB_ICONERROR);
#else
        PawsManager::GetSingleton().CreateWarningBox(error.GetData());
        psupdaterengine->OutToScreen("Exiting in 10 seconds...");
        csSleep(10000);
#endif
    }

    PS_PAUSEEXIT(EXIT_FAILURE);
}

void psUpdaterEngine::InstallDirectory(const char* path,const char* to)
{
#ifdef CS_PLATFORM_WIN32
    csRef<iStringArray> files = vfs->FindFiles("/this/" + csString(path));

    // Copy all files
    printf("Installing %d files..\n",(int)files->Length());
    for (size_t i = 0;i < files->Length();i++)
    {
        csString fileName = files->Get(i);
        fileName = fileName.Slice(6,fileName.Length()-6);
        
        csString newp;
        newp.Format("%s\\%s",to,fileName.GetData());
        
        // Win32 API

        fileName.ReplaceAll("/","\\"); // Windows uses \ 
        newp    .ReplaceAll("/","\\"); // WIndows uses \ 

        if(GetFileAttributes(fileName.GetData()) & FILE_ATTRIBUTE_DIRECTORY)
        {
            updater::MakeDirectory(to + csString("\\") + fileName);

            fileName.ReplaceAll("\\","/"); // VFS uses /
            InstallDirectory(fileName,to);
            continue;
        } 

        printf("Installing file %s..\n",fileName.GetData());
        if(CopyFile(fileName.GetData(),newp.GetData(),false) == 0)
        {
            /* And some more cryptical Win32 API to get the error message :\*/
            char buf[80]; LPVOID msgBuf; DWORD dw = GetLastError(); 

            FormatMessage( 
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                NULL,
                dw,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &msgBuf,
                0, NULL );

            wsprintf(buf,"%s", msgBuf); 

            // Make it csString
            csString error = buf;
            error = error.Slice(0,error.Length() -3);

            csString msg;
            msg.Format("Couldn't copy file %s!\n%s (%d)",fileName.GetData(),error.GetData(),dw);
            LocalFree(msgBuf);

            int ret = MessageBox(NULL, msg.GetData(), "File copy error", MB_ICONERROR | MB_RETRYCANCEL);

            if(ret == IDRETRY)
            {
                i--;
                continue;
            }
        }
    }
#endif
}

void psUpdaterEngine::Install(const char* to)
{
#ifdef CS_PLATFORM_WIN32

    // Give time to the previous installer to close
    Sleep(5000);
    // Copy files from updatertemp to main dir
    InstallDirectory("",to);
    if(!LaunchProgram(csString(to) + "\\updater.exe -autolaunch",to))
        MessageBox(0,"Autolaunch of the new updater failed.\nPlease restart the updater manualy","Updater - Selfupdating",MB_ICONINFORMATION);
#endif
}

/************************************************************/

int main(int argc, char** argv)
{
    bool auto_sw  = false;
    bool compare  = false;
    bool addresses= true;
    bool autolaunch = false;

    csString create;
    csString updateRepo,file,mirror,install;

    //Check for silent and if we should create a new resp
    csRef<csCommandLineParser> cmdParser = csPtr<csCommandLineParser> (new csCommandLineParser);
    cmdParser->Initialize(argc,argv);

    // Help?
    if(cmdParser->GetBoolOption("help"))
    {
        csString ver; ver = UPDATER_VERSION;
        for(size_t i = 1;i < ver.Length();i++)
        {
            ver.Insert(i,'.');
            i++; // Jump twice
        }

        printf("\nPlaneShift updater version %s\n",ver.GetData());
        printf("Help section\n");
        printf("===============================================================================\n");
        printf("   -auto                 Switches to automatic mode without a GUI\n");
        printf("   -create=/path/        Creates a new repository in the path specified\n");
        printf("   -compare              Compares your files and the default mirrors and prints\n"); 
        printf("                         which files that you need to update or create to make\n");
        printf("                         the repository exactly as your folder\n");
        printf("   -updaterep=/repos/    Adds a file('-file=?') to a repository (Local use only)\n");
        printf("   -addresses            Prints a list of files to download instead of\n");
        printf("                         downloading them. You can specify the mirror to\n");
        printf("                         use with '-mirror=?'\n");
        printf("   -autolaunch           Auto start the updater (not needed with -auto)\n");
        exit(0);
    }

    auto_sw     = cmdParser->GetBoolOption("auto");
    create      = cmdParser->GetOption("create");
    compare     = cmdParser->GetBoolOption("compare");
    updateRepo  = cmdParser->GetOption("updaterep");
    addresses   = cmdParser->GetBoolOption("addresses");
    mirror      = cmdParser->GetOption("mirror"); // If we use addresses, otherwise no use
    install     = cmdParser->GetOption("install"); // Not listed in help since it's only for internal use
    autolaunch  = cmdParser->GetBoolOption("autolaunch");

    if(updateRepo != "")
    {
        file   = cmdParser->GetOption("file");
        if(file == "")
        {
            printf("Trying to update the repository but no file option. Ignoring");
            updateRepo = "";
        }
        else
            auto_sw = true;
    }

    //We will be in silent mode when creating or updating the updater
    if (create.Length() > 0)
        auto_sw = true;

    if (compare)
        auto_sw = true;

    if (addresses)
        auto_sw = true;
    
    if(install.Length() > 0)
        auto_sw = true;

    //Create the standard stuff
    iObjectRegistry* objreg = csInitializer::CreateEnvironment (argc, argv);

    // Create our application object
    psupdaterengine = new psUpdaterEngine(objreg,auto_sw);

    // Initialize Crystal space for the engine
    if (!psupdaterengine->InitializeCS())
    {             
        csReport (objreg, CS_REPORTER_SEVERITY_ERROR,
                  PSAPP, "Failed to init Crystal Space for app!");
        PS_PAUSEEXIT(1);
    }
    
    // Initialize engine (skip if we're in install mode, need to load as little as possible)
    if(!install.Length())
    {
        if (!psupdaterengine->Initialize())
        {             
            csReport (objreg, CS_REPORTER_SEVERITY_ERROR,
                    PSAPP, "Failed to init app!");
            PS_PAUSEEXIT(1);
        }
    }

#if defined(CS_PLATFORM_WIN32) && defined(NDEBUG)
    if(!cmdParser->GetBoolOption("console") && !auto_sw)
    {
        FreeConsole();
    }
#endif

    // Do some cleanup
    if(psupdaterengine->GetVFS()->Exists("/this/updatertemp"))
        psupdaterengine->GetUpdater()->CleanUp();

    // Begin argument specific code
    if (create.Length() > 0)
    {
        // Add the destination dir to the omit list to prevent scanning itself
        csString dirName(create);
        if (dirName.GetAt(0) == '/')
            dirName.DeleteAt(0);
        if (dirName.GetAt(dirName.Length()-1) == '/')
            dirName.DeleteAt(dirName.Length()-1);
        if (dirName.StartsWith("this",true))
            dirName.DeleteAt(0,5);
        psupdaterengine->GetUpdater()->GetConfig()->AddDirOmit(dirName);

        psupdaterengine->SetCreateMode();
        psupdaterengine->GetUpdater()->CreateRegistry(objreg,create);
        psupdaterengine->QuitClient();
    }
    else if (updateRepo.Length() > 0)
    {
        RegistryCreator* creator = new RegistryCreator(objreg);
        creator->UpdateRegistry(updateRepo,file);
        psupdaterengine->QuitClient();
    }
    else if (compare)
    {
        psupdaterengine->GetUpdater()->Compare();
        psupdaterengine->QuitClient();
    }
    else if (install.Length() > 0)
    {
    // Only used in windows
#ifdef CS_PLATFORM_WIN32
        psupdaterengine->Install(install);
        psupdaterengine->QuitClient();
#endif
    }
    else if (addresses)
    {
        int mirrori = 0;
        if(!mirror)
             mirrori = psupdaterengine->GetUpdater()->GetConfig()->GetMirror()->GetID();
        else
        {
            mirrori = atoi(mirror);
            psupdaterengine->GetUpdater()->GetConfig()->SetMirror(mirrori);
        }
        
        ChangeList list;
        psupdaterengine->GetUpdater()->DownloadRegistry();

        printf("Checking for updates..\n");
        psupdaterengine->GetUpdater()->CheckUpdates(list);
        psupdaterengine->GetUpdater()->CreateToDoList(&list,mirrori);       
    }
    else if (auto_sw)
    {
        psupdaterengine->GetUpdater()->StartUpdate();
    }
    else if(autolaunch)
    {
        psupdaterengine->GetUpdater()->StartUpdate();
    }
        

    // start the main event loop
    csDefaultRunLoop(objreg);

    // remove engine before destroying Application
    delete psupdaterengine;
    psupdaterengine = NULL;
    
    csInitializer::DestroyApplication (objreg);

    return 0;
}
