// This file is a part of the xMule Project.
// 
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
// 
// Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.xmule-project.net )
// 
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#ifdef PRECOMP
    #include "xmule-headers.h"
#endif

#include "xmule.h"                          // Needed for this Interface's Prototype

#include "ChatWnd.h"                        // Needed for CChatWnd
#include "ClientCredits.h"                  // Needed for CClientCredits
#include "ClientList.h"                     // Needed for CClientList
#include "config.h"                         // Needed for HAVE_SYS_RESOURCE_H
#include "DownloadListCtrl.h"               // Needed for CDownloadListCtrl
#include "DownloadQueue.h"                  // Needed for CDownloadQueue
#include "ED2KLink.h"                       // Needed for CED2KLink
#include "FlowChart.h"                      // Needed for CFlowChart
#include "FriendList.h"                     // Needed for CFriendList
#include "IPFilter.h"                       // Needed for CIPFilter
#include "KnownFile.h"                      // Needed for CKnownFile
#include "KnownFileList.h"                  // Needed for CKnownFileList
#include "ldaemon.h"                        // Needed for CLDaemon
#include "ListenSocket.h"                   // Needed for CListenSocket
#include "muuli_wdr.h"                      // Needed for ID_FRIENDLIST
#include "NewFunctions.h"                   // Needed for MAP
#include "NewSockets.h"                     // Needed for NewSocket_Start
#include "otherfunctions.h"                 // Needed for CastItoXBytes
#include "Preferences.h"                    // CPreferences::List Settings
#include "PreferencesDlg.h"                 // Needed for CPreferencesDlg
#include "QueueListCtrl.h"                  // Needed for CQueueListCtrl
#include "server.h"                         // Needed for CServer
#include "ServerList.h"                     // Needed for CServerList
#include "ServerListCtrl.h"                 // Needed for CServerListCtrl
#include "ServerWnd.h"                      // Needed for CServerWnd
#include "SharedFileList.h"                 // Needed for CSharedFileList
#include "SharedFilesCtrl.h"                // Needed for CSharedFilesCtrl
#include "SharedFilesWnd.h"                 // Needed for CSharedFilesWnd
#include "sockets.h"                        // Needed for CServerConnect
#include "StatisticsDlg.h"                  // Needed for CStatisticsDlg
#include "TransferWnd.h"                    // Needed for CTransferWnd
#include "updownclient.h"                   // Needed for CUpDownClient
#include "UploadListCtrl.h"                 // Needed for CUploadListCtrl
#include "UploadQueue.h"                    // Needed for CUploadQueue
#include "xmuleDlg.h"                       // Needed for CxmuleDlg

#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences
#include <DynPrefs/DynPrefsCtrl.h>          // Needed for wxEVT_PREFS_CHANGED

#include <iostream>                         // Needed for std::cout
#include <gtk/gtk.h>                        // Needed for GTK_WINDOW_TOPLEVEL
#include <mcheck.h>

#include <wx/clipbrd.h>                     // Needed for wxTheClipboard
#include <wx/msgdlg.h>                      // Needed for wxMessageBox
#include <wx/splash.h>                      // Needed for wxSplashScreen

#include <wx/xrc/xmlres.h>                  // Needed for wxXmlResource

#include <unistd.h>                         // Needed for close

using std::cout;
using std::endl;

IMPLEMENT_APP(CxmuleApp)

void InitSplashXRC();

#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif

#ifdef __GLIBC__
# define RLIMIT_RESOURCE __rlimit_resource
#else
# define RLIMIT_RESOURCE int
#endif

//MAP* listof_sourcesKF;

static void UnlimitResource(RLIMIT_RESOURCE resType)
{
#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
    struct rlimit rl;
    getrlimit(resType, & rl);
    rl.rlim_cur = rl.rlim_max;
    setrlimit(resType, & rl);
#endif
}

static void SetResourceLimits()
{
#ifdef HAVE_SYS_RESOURCE_H
    UnlimitResource(RLIMIT_DATA);
    UnlimitResource(RLIMIT_FSIZE);
    UnlimitResource(RLIMIT_NOFILE);
    UnlimitResource(RLIMIT_RSS);
#endif
}

CxmuleApp::CxmuleApp()
{
}

CxmuleApp::~ CxmuleApp()
{
    listensocket->Destroy();
}

int CxmuleApp::OnExit()
{
    delete uploadqueue;
    cout << "xMule shutdown completed." << endl;
    return wxApp::OnExit();
}

extern void InitXmlResource();

bool CxmuleApp::OnInit()
{
//    mtrace();
    daemon = nowindow = false;
    for (int i = 1 ; i < argc ; i++)
    {
        if (strncmp(argv[i], "--version", 7) == 0)
        {
            printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
            exit(0);
        }
        else if (strncmp(argv[i], "--daemon", 8) == 0)
        {
            printf ("Starting without windows\n");
            daemon = true;
        }
    }
//    MapData_Init(listof_sourcesKF);
    wxHandleFatalExceptions(true);
    SetResourceLimits();
    // for resources
    wxXmlResource::Get()->InitAllHandlers();
    InitXmlResource();
    printf("Initialising xMule\n");
    close(0);
    SetVendorName("TikuWarez");

    // Do NOT change this string to xMule nor anything else, it WILL fuck you up.
    SetAppName("eMule");
    m_locale.Init(wxLANGUAGE_DEFAULT);
    m_locale.AddCatalogLookupPathPrefix(LOCALEDIR);
    m_locale.AddCatalog(PACKAGE_NAME);

    // Madcat - Check if prefs can be found at ~/.lmule and rename as neccesery.
    wxString OldPrefDir = getenv("HOME") + wxString("/.lmule");
    wxString NewPrefDir = getenv("HOME") + wxString("/.xMule");
    if (wxDirExists(OldPrefDir) && !wxDirExists(NewPrefDir))
    {
        printf("Found old settings, moving to new dir.\n");
        wxRenameFile(OldPrefDir, NewPrefDir);
    }
    if (!wxDirExists(NewPrefDir))
    {
        wxMkdir(NewPrefDir, 0777);
    }
    if (!wxDirExists(NewPrefDir + wxString("/Incoming")))
    {
        wxMkdir(NewPrefDir + wxString("/Incoming"), 0777);
    }
    if (!wxDirExists(NewPrefDir + wxString("/Temp")))
    {
        wxMkdir(NewPrefDir + wxString("/Temp"), 0777);
    }
    wxString filename = NewPrefDir + wxString("/server.met");
    if (!wxFileExists(filename))
    {
        wxFile metfile(filename, wxFile::write_excl);
        if (metfile.IsOpened())
        {
            metfile.Close();
        }
    }
    CxmuleDlg * dlg = new CxmuleDlg(NULL, wxString::Format(wxT("%s v%s"), wxT(PACKAGE_NAME), wxT(PACKAGE_VERSION)));
    xmuledlg = dlg;
    if (nowindow == false)
    {
        dlg->Show(TRUE);
    }
    // Delete old log file.
    wxRemoveFile(wxString::Format("%s/.xMule/logfile", getenv("HOME")));
    dynprefs = new DynamicPreferences;
    glob_prefs = new CPreferences();
    xmuledlg->flowchartwnd = new CFlowChart(xmuledlg, dynprefs);
    xmuledlg->flowchartwnd->Show(false);
    LoadCompatiblePrefs();
    xmuledlg->preferenceswnd->SetPrefs(glob_prefs);

    dynprefs->Add("daemon", daemon);
/*
-----BEGIN PGP MESSAGE-----
Charset: UTF-8
Version: GnuPG v1.2.2
Comment: This is a special comment that is never compiled.

hQHOA2jcsHQEewHzEAcAjkfYTfMXMLjXwl/H92G6YV9ioEGOTK+mzQnkOUpk1Uht oyf5P4iz7bQc4UK71D+s3CFNMKirEa1Ho5+MXeFczHdqMAyUYiWyB5dMaTSTwOdp
XI+/oMTo4imftEmak+h0SGBzagAVjRr5jY7EDSky/9EA3WWI7hN5b7k+nKZu+W4O s62KEwp8pEkqYxNH4GWGjA0xXgP3E/i6NNfaKQgo895fvHsPYLXTmMgVXf+W0TUN
NvcxuysflrHb2eqpiu4K2TUcq8O8qr59Dnb2h4zgy7BCNe8IxKu2Ws7pqryrhDsG /3RSzus+HO4dD1yl74p7nYjLTKYoMwgSTg4qoVRGBhVjGanxJ58/VFqIc3rYWjgq
q09U1amZaD0D64VzfZVe2VuN7KUlcQTK/BiAs16uuHLDbhuID6CUUTPSmnp0hL57 Lkg8MfRzI/HLSFzXlauK3hP1nNBdoFCnxaCkkXTBHVYViIbR0umBCCcTqFN3Gesr
le/x5AQfdJUBy9vJqojC9UydwWR7fZDaP2kq0ddk9ZQePmcyMTmspXmZqN3yHAhz 3Z5GYEuATi9iVbYlLC0mKB/VhYnwD+lQeodaxluX/FFs0sCHAefz63ge/Y7oQ0JO
8hX+FfdnPWZ1RGtDkXK7nvpfg03wPTpTpMPNEvFzwOir7FGpqyapTXwq8BuOWEyn FDw2gpmdkGSANTP9seVj++JZr631D6xIn/xqyvY0Tg8CSZLam6iAZ/45P7KsvWnA
WZ8k6XmfsLqx5hOxTin1AJilmFnQDDwpT9Ut6DUo66yFdVdpc88yDutKvFFFnp4N 2Kfg2u3BNmaxNuyxyRJL5AqQAgLz47P3vn58f+IStOL9HoHemnaE+368AMzmGUAf
xgDi99qMpDDUabceIP22Vwwxeuyqmn5FkPNVS3Tf+YnxYimVr846SUAumaYL74bz yd9fg0AQZs1ptlDZZqTBvOnQaJjIDRqPpoqczqthAfFs+byQSbXEndh3UU0zDzWU
hu+JvF+Z2knW4lRM7VrainKZOdpEvT3EKEBI
=BUaZ
-----END PGP MESSAGE-----
*/
    if (dynprefs->Get<bool>("daemon") == false)
    {
        xmuledlg->createSystray(wxString::Format(wxT("%s v%s"), wxT(PACKAGE_NAME), wxT(PACKAGE_VERSION)));
    }

    if (dynprefs->Get<bool>("daemon") == false &&
/*->*/  theApp.dynprefs->Get<bool>("show-splashscreen") && 
/*->*/  theApp.dynprefs->Get<bool>("start-minimized") == false)
    {
        InitSplashXRC();
        wxImage::AddHandler(new wxJPEGHandler);
        wxFileSystem fsys;
        wxFSFile* f = fsys.OpenFile(wxT("memory:About.jpg"));
        wxSplashScreen* splsh = new wxSplashScreen(wxImage(*f->GetStream(), wxBITMAP_TYPE_JPEG), wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 5000, NULL, - 1, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER | wxSTAY_ON_TOP);
    }

    if (dynprefs->Get<bool>("command-line") == true)
    {
        lda = new CLDaemon;
        lda->Init();
    }

    clientlist = new CClientList();
    friendlist = new CFriendList();
    knownfiles = new CKnownFileList(const_cast<char *>(dynprefs->Get<wxString>("app-directory").c_str()));
    serverlist = new CServerList(glob_prefs);
    serverconnect = new CServerConnect(serverlist, theApp.glob_prefs);
    sharedfiles = new CSharedFileList(glob_prefs, serverconnect, knownfiles);
//    printf("*** TCP socket at %d\n", theApp.dynprefs->Get<long>("tcp-port"));
    wxIPV4address myaddr;
    myaddr.AnyAddress();
    myaddr.Service(theApp.dynprefs->Get<long>("tcp-port"));
    NewSocket_Start(1, 0, theApp.dynprefs->Get<long>("udp-port"));

    // bugfix - do this before creating the uploadqueue:
    downloadqueue = new CDownloadQueue(glob_prefs, sharedfiles);
    uploadqueue = new CUploadQueue(glob_prefs);
    ipfilter = new CIPFilter();
    clientcredits = new CClientCreditsList(glob_prefs);
//    webserver = new CWebServer();
    xmuledlg->statisticswnd->Init();
    xmuledlg->statisticswnd->ShowInterval();
    // must do initialisations here..
    xmuledlg->serverwnd->serverlistctrl->Init(serverlist);
    serverlist->Init();
    theApp.downloadqueue->Init();
    xmuledlg->AddLogLine(true, PACKAGE_STRING);
    //
    theApp.sharedfiles->SetOutputCtrl((CSharedFilesCtrl *) xmuledlg->sharedfileswnd->FindWindowByName("sharedFilesCt"));
    // then init firend list
    theApp.friendlist->SetWindow((CFriendListCtrl *) theApp.xmuledlg->chatwnd->FindWindowById(ID_FRIENDLIST));
    theApp.friendlist->ShowFriends();
    SetTopWindow(dlg);
    // reset statistic values
    theApp.stat_sessionReceivedBytes = 0;
    theApp.stat_sessionSentBytes = 0;
    theApp.stat_reconnects = 0;
    theApp.stat_transferStarttime = 0;
    theApp.stat_serverConnectTime = 0;
    theApp.Start_time = GetTickCount();
    // Initialize and sort all lists.
    // FIX: Remove from here and put these back to the OnInitDialog()s
    // and call the OnInitDialog()s here!
    theApp.xmuledlg->transferwnd->downloadlistctrl->LoadSettings(CPreferences::tableDownload);
    theApp.xmuledlg->transferwnd->downloadlistctrl->InitSort();
    theApp.xmuledlg->transferwnd->uploadlistctrl->LoadSettings(CPreferences::tableUpload);
    theApp.xmuledlg->transferwnd->uploadlistctrl->InitSort();
    theApp.xmuledlg->transferwnd->queuelistctrl->LoadSettings(CPreferences::tableQueue);
    theApp.xmuledlg->transferwnd->queuelistctrl->InitSort();
    theApp.xmuledlg->serverwnd->serverlistctrl->LoadSettings(CPreferences::tableServer);
    theApp.xmuledlg->serverwnd->serverlistctrl->InitSort();
    theApp.xmuledlg->sharedfileswnd->sharedfilesctrl->LoadSettings(CPreferences::tableShared);
    theApp.xmuledlg->sharedfileswnd->sharedfilesctrl->InitSort();
    // call the initializers
    theApp.xmuledlg->transferwnd->OnInitDialog();
    xmuledlg->m_app_state = APP_STATE_RUNNING;
    theApp.sharedfiles->Reload(true, true);
/*    if (glob_prefs->GetWSIsEnabled())
    {
        webserver->ReloadTemplates();
        webserver->StartServer();
    }*/

    listensocket = new CListenSocket(glob_prefs, myaddr);
    if (!listensocket->Ok())
    {
        xmuledlg->AddLogLine(false, (_("Port %d is not available. You will be LOWID")), theApp.dynprefs->Get<long>("tcp-port"));
        wxString str;
        str.Printf(_("Port %d is not available !!\n\nThis will mean that you will be LOWID.\n\nUse netstat to determine when port becomes available\nand try starting xmule again."), theApp.dynprefs->Get<long>("tcp-port"));
        wxMessageBox(str, _("Error"), wxCENTRE | wxOK | wxICON_ERROR);
    }

    // pretty much ready now. start autoconnect if
    if (theApp.dynprefs->Get<bool>("autoconnect") == true)
    {
        wxCommandEvent event;
        theApp.xmuledlg->OnBnConnect(event);
    }
    return TRUE;
}

void CxmuleApp::ProcessCommand()
{
    uint32_t num,i;
    wxString St1;
    bool notunderstood=true;

    lda->Print (wxString ("Processing command '") + lda->Command + wxString("'"),LD_OUTPUT,true);

    if ((lda->Command.Left (8)) == wxString ("connect "))
    {
        St1=lda->Command.AfterFirst (32);
        notunderstood = false;

        CServer* cur_file;
        for (int a = 0; a < theApp.serverlist->GetServerCount(); ++a)
        {
            cur_file = theApp.serverlist->GetNextServer();
            if (St1 == wxString (cur_file->GetListName()))
            {
                serverconnect->ConnectToServer(cur_file);
                lda->Print(wxString ("Found ") + St1 + wxString (" server. Trying connection"), LD_OUTPUT);
                break;
            }
        }
        delete cur_file;
    }
    else if ((lda->Command.Left (4)) == wxString ("exit"))
    {
        lda->Print ("Exiting xmule",LD_OUTPUT);
        xmuledlg->Close ();
        notunderstood=false;
    }
    else if ((lda->Command.Left (7)) == wxString ("window "))
    {
        St1=lda->Command.AfterFirst (32);
        if (St1 == wxString ("show"))
        {
            nowindow=false;
            notunderstood=false;
            xmuledlg->Show ();
        }
        else if (St1 == wxString ("hide"))
        {
            nowindow=true;
            notunderstood=false;
            xmuledlg->Hide ();
        }
    }
    else if ((lda->Command.Left (4)) == wxString ("help"))
    {
 	notunderstood=false;
	lda->Print ("Available commands are:",LD_OUTPUT);
	lda->Print ("\tconnect <server_name>\tConnect to server",LD_OUTPUT);
	lda->Print ("\texit\t\t\tClose xMule",LD_OUTPUT);
	lda->Print ("\thelp\t\t\tShow this help",LD_OUTPUT);
	lda->Print ("\twindow [show|hide]\tShow or Hide the xMule window",LD_OUTPUT);
    }

    if (notunderstood)
    {
        lda->Print (wxString ("Command ") + lda->Command + wxString(" not understood"),LD_OUTPUT);
	lda->Print ("Use command 'help' for get help",LD_OUTPUT);
    }
    lda->Command="";
}

void CxmuleApp::RefreshFiles()
{
    int i,num;
    CUpDownClient *client;
    CKnownFile *file;
    char buffer[150],username[20],filename[100];
    wxString buf;

    //
    // Upload
    //
    num=xmuledlg->transferwnd->uploadlistctrl->GetItemCount ();
    lda->Delete (LD_UPLO);
    if (num)
    {
        lda->Print ("------------------ ---------------------------------------------------------------------------------- ----------- ----------",LD_UPLO);
        lda->Print ("   Client Name                                     File Name                                             Speed    Transfered",LD_UPLO);
        lda->Print ("------------------ ---------------------------------------------------------------------------------- ----------- ----------",LD_UPLO);
        for (i=0;i<num;++i)
        {
            client = (CUpDownClient *)(xmuledlg->transferwnd->uploadlistctrl->GetItemData (i));
            if (!client)
                return;

            file = sharedfiles->GetFileByID(client->GetUploadFileID());
            strncpy (username,client->GetUserName(),18);
            username[18]=0;
            strncpy (filename,file->GetFileName(),82);
            filename[82]=0;

            sprintf (buffer,"%-18s %-82s %6.1f kB/s %10s",username,filename,(float) client->GetDatarate() /1024,CastItoXBytes(client->GetTransferedUp()).GetData());
            lda->Print (wxString (buffer),LD_UPLO);
        }
    }

    //
    // Download
    //
    buf=xmuledlg->transferwnd->downloadlistctrl->getListforDaemon();
    lda->Delete (LD_DOWN);
    if (buf.Len())
    {
        lda->Print (wxString ("------------------------------------------------------------ ---------- --------- ----------- ------- --------- ------------"),LD_DOWN);
        lda->Print (wxString ("                       FILE NAME                                SIZE    COMPLETED    SPEED    PERCENT   FONTS      STATE"),LD_DOWN);
        lda->Print (wxString ("------------------------------------------------------------ ---------- --------- ----------- ------- --------- ------------"),LD_DOWN);
        lda->Print (buf,LD_DOWN);
    }
}

void CxmuleApp::UpdateReceivedBytes(int32_t bytesToAdd)
{
    SetTimeOnTransfer();
    stat_sessionReceivedBytes += bytesToAdd;
}

void CxmuleApp::UpdateSentBytes(int32_t bytesToAdd)
{
    SetTimeOnTransfer();
    stat_sessionSentBytes += bytesToAdd;
}

void CxmuleApp::SetTimeOnTransfer()
{
    if (stat_transferStarttime > 0)
    return;
    stat_transferStarttime = GetTickCount();
}

typedef char * LPTSTR;

wxString CxmuleApp::StripInvalidFilenameChars(wxString strText, bool bKeepSpaces)
{
    LPTSTR pszBuffer = (char *) strText.GetData();
    LPTSTR pszSource = pszBuffer;
    LPTSTR pszDest = pszBuffer;
    while ( * pszSource != '\0')
    {
        // lots of invalid chars for filenames in windows :=):
        if (! (( * pszSource <= 31 && * pszSource >= 0) ||
        * pszSource == '\"' || * pszSource == '*' || * pszSource == '<' || * pszSource == '>' || * pszSource == '?' || * pszSource == '|' || * pszSource == '\\' || * pszSource == '/' || * pszSource == ':'))
        {
            if (!bKeepSpaces && * pszSource == ' ')
            {
                * pszDest = '_';
            }
            else
            {
                * pszDest = * pszSource;
            }
            pszDest++;
        }
        pszSource++;
    }
    * pszDest = '\0';
    return strText;
}

wxString CxmuleApp::CreateED2kLink(CAbstractFile * f)
{
    wxString strLink;
    // spaces to dots:
    strLink.Printf("ed2k://|file|%s|%d|%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x|/", StripInvalidFilenameChars(f->GetFileName(), true) .GetData(),
    f->GetFileSize(), f->GetFileHash() [0], f->GetFileHash() [1], f->GetFileHash() [2], f->GetFileHash() [3], f->GetFileHash() [4], f->GetFileHash() [5], f->GetFileHash() [6], f->GetFileHash() [7], f->GetFileHash() [8], f->GetFileHash() [9], f->GetFileHash() [10], f->GetFileHash() [11], f->GetFileHash() [12], f->GetFileHash() [13], f->GetFileHash() [14], f->GetFileHash() [15]);
    return strLink.GetData();
}

wxString CxmuleApp::CreateED2kSourceLink(CAbstractFile * f)
{
    if (!serverconnect->IsConnected() || serverconnect->IsLowID())
    {
        xmuledlg->AddLogLine(true, GetResString(IDS_SOURCELINKFAILED));
        return wxString("");
    }
    uint32_t dwID = serverconnect->GetClientID();
    wxString strLink;
    // spaces to dots:
    strLink.Printf("ed2k://|file|%s|%u|%s|/|sources,%i.%i.%i.%i:%i|/", StripInvalidFilenameChars(f->GetFileName(), false) .GetData(),
    f->GetFileSize(), EncodeBase16(f->GetFileHash(), 16) .GetData(), (uint8_t) dwID, (uint8_t)(dwID >> 8), (uint8_t)(dwID >> 16), (uint8_t)(dwID >> 24), theApp.dynprefs->Get<long>("tcp-port"));
    return strLink;
}

wxString CxmuleApp::CreateHTMLED2kLink(CAbstractFile * f)
{
    wxString strCode = "<a href=\"" + CreateED2kLink(f) + "\">" + StripInvalidFilenameChars(f->GetFileName(), true) + "</a>";
    return strCode;
}

bool CxmuleApp::CopyTextToClipboard(wxString strText)
{
    if (wxTheClipboard->Open())
    {
        wxTheClipboard->UsePrimarySelection(TRUE);
        wxTheClipboard->SetData(new wxTextDataObject(strText));
        wxTheClipboard->Close();
    }
}

/* Original implementation by Bouc7 of the eMule Project.
 xMule Signature idea was designed by BigBob and implemented
 by Un-Thesis, with design inputs and suggestions from bothie.
*/

void CxmuleApp::OnlineSig(bool zero)
{
    // Do not do anything if online signature is disabled in Preferences
    if (theApp.dynprefs->Get<bool>("online-signature") == false)
    {
        return;
    }
    // Open both files for writing
    wxFile xmulesig_out, emulesig_out;
    if (!emulesig_out.Open(dynprefs->Get<wxString>("app-directory") + wxT("onlinesig.dat"), wxFile::write))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERROR_SAVEFILE) + wxString(" OnlineSig File"));
    }
    if (!xmulesig_out.Open(dynprefs->Get<wxString>("app-directory") + wxT("xmulesig.dat"), wxFile::write))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERROR_SAVEFILE) + wxString(" xMule OnlineSig File"));
    }
    char buffer[256];
    if (zero)
    {
        emulesig_out.Write("0\n0.0|0.0|0\n", 12);
        xmulesig_out.Write("0\n0\n0\n0\n0\n0.0\n0.0\n0\n0\n", 22);
    }
    else
    {
        if (serverconnect->IsConnected())
        {
            // We are online
            emulesig_out.Write("1", 1);
            emulesig_out.Write("|", 1);
            // Name of server (Do not use GetRealName()!)
            emulesig_out.Write(serverconnect->GetCurrentServer()->GetListName(), strlen(serverconnect->GetCurrentServer()->GetListName()));
            emulesig_out.Write("|", 1);
            // IP and port of the server
            emulesig_out.Write(serverconnect->GetCurrentServer()->GetFullIP(), strlen(serverconnect->GetCurrentServer()->GetFullIP()));
            emulesig_out.Write("|", 1);
            sprintf(buffer, "%d", serverconnect->GetCurrentServer()->GetPort());
            emulesig_out.Write(buffer, strlen(buffer));
            // Now for xmule sig
            xmulesig_out.Write("1", 1);
            xmulesig_out.Write("\n", 1);
            xmulesig_out.Write(serverconnect->GetCurrentServer()->GetListName(), strlen(serverconnect->GetCurrentServer()->GetListName()));
            xmulesig_out.Write("\n", 1);
            xmulesig_out.Write(serverconnect->GetCurrentServer()->GetFullIP(), strlen(serverconnect->GetCurrentServer()->GetFullIP()));
            xmulesig_out.Write("\n", 1);
            xmulesig_out.Write(buffer, strlen(buffer));
            xmulesig_out.Write("\n", 1);
            // Low- or High-ID (only in xmule sig)
            if (theApp.serverconnect->IsLowID())
            {
                xmulesig_out.Write("L\n", 2);
            }
            else
            {
                xmulesig_out.Write("H\n", 2);
            }
        }
        else
        {
            // Not connected to a server
            emulesig_out.Write("0", 1);
            xmulesig_out.Write("0\n0\n0\n0\n0\n", 10);
        }
        emulesig_out.Write("\n", 1);
        // Datarate for downloads
        sprintf(buffer, "%.1f", (float) downloadqueue->GetDatarate() / 1024);
        emulesig_out.Write(buffer, strlen(buffer));
        emulesig_out.Write("|", 1);
        xmulesig_out.Write(buffer, strlen(buffer));
        xmulesig_out.Write("\n", 1);
        // Datarate for uploads
        sprintf(buffer, "%.1f", (float) uploadqueue->GetDatarate() / 1024);
        emulesig_out.Write(buffer, strlen(buffer));
        emulesig_out.Write("|", 1);
        xmulesig_out.Write(buffer, strlen(buffer));
        xmulesig_out.Write("\n", 1);
        // Number of users waiting for upload
        sprintf(buffer, "%d", uploadqueue->GetWaitingUserCount());
        emulesig_out.Write(buffer, strlen(buffer));
        xmulesig_out.Write(buffer, strlen(buffer));
        xmulesig_out.Write("\n", 1);
        // Number of shared files
        sprintf(buffer, "%d", theApp.sharedfiles->GetCount());
        xmulesig_out.Write(buffer, strlen(buffer));
        xmulesig_out.Write("\n", 1);
    }
    /* if (!zero) */
    // Nick on the network
    sprintf(buffer, "%s", const_cast<char *>(dynprefs->Get<wxString>("nickname").c_str()));
    xmulesig_out.Write(buffer, strlen(buffer));
    xmulesig_out.Write("\n", 1);
    // Total received in GB
    sprintf(buffer, "%.2f", (float)(theApp.stat_sessionReceivedBytes + theApp.glob_prefs->GetTotalDownloaded()) / 1073741824);
    xmulesig_out.Write(buffer, strlen(buffer));
    xmulesig_out.Write("\n", 1);
    // Total sent in GB
    sprintf(buffer, "%.2f", (float)(theApp.stat_sessionSentBytes + theApp.glob_prefs->GetTotalUploaded()) / 1073741824);
    xmulesig_out.Write(buffer, strlen(buffer));
    xmulesig_out.Write("\n", 1);
    // xmule version
    sprintf(buffer, "%s", PACKAGE_VERSION);
    xmulesig_out.Write(buffer, strlen(buffer));
    xmulesig_out.Write("\n", 1);
    // Close the files
    emulesig_out.Close();
    xmulesig_out.Close();
    //End Added By Bouc7:
}

#if defined(__linux__)
    #include <execinfo.h>                   // Needed for backtrace
    #include <string>                       // Needed for std::string
    #include <iostream>                     // Needed for std::cerr
    #include <cxxabi.h>                     // Needed for __cxa_demangle
    #include <wx/utils.h>                   // Needed for wxExecute
#endif

void CxmuleApp::OnFatalException()
{
    // Close sockets first.
    if (theApp.listensocket)
    {
        theApp.listensocket->Destroy();
        NewSocket_Stop(1);
    }

// (stkn) create backtrace
#if defined(__linux__)
    int status;
    char* realname;
    
    // 100 should be enough ?!?:
    void * bt_array[100];
    char ** bt_strings;
    int num_entries;

    if ((num_entries = backtrace(bt_array, 100)) < 0)
    {
        cerr << "* Could not generate backtrace" << endl;
        return;
    }

    if ((bt_strings = backtrace_symbols(bt_array, num_entries)) == NULL)
    {
        cerr << "* Could not get symbol names for backtrace" << endl;
        return;
    }

    cerr << "\nOOPS! - Seems like xMule crashed\n--== BACKTRACE FOLLOWS: ==--" << endl << endl;

    std::vector<std::string> functions;
    std::vector<std::string> addresses;
    std::vector<std::string> lines;

    std::string addr_str;

    for (int i = 0 ; i < num_entries ; i++)
    {
        std::string mangle(bt_strings[i]);
        std::string address;

        int firstparen, secondparen, firstbrack;


        firstparen = mangle.find("(_Z");
        secondparen = mangle.rfind('+');
        firstbrack = mangle.rfind('[');

        if (firstbrack != string::npos)
        {
            address = mangle.substr(firstbrack + 1, mangle.rfind(']') - firstbrack - 1);
            addr_str = addr_str + " " + address;
            addresses.push_back(address);
        }

        if (secondparen != string::npos)
        {
            if (firstparen != string::npos)
            {
                mangle = mangle.substr(firstparen + 1, secondparen - firstparen - 1);
                mangle = abi::__cxa_demangle(mangle.c_str(), 0, 0, &status);
            }
            else
            {
                firstparen = mangle.find('(');

                mangle = mangle.substr(firstparen + 1, secondparen - firstparen - 1);
            }
        }

        functions.push_back(mangle);
    }

    wxArrayString output;
    pid_t self_pid = getpid();
    std::string line;

    wxExecute(wxString::Format("addr2line -e /proc/%d/exe %s", self_pid, addr_str.c_str()), output);

    for (int a=0; a<output.GetCount(); ++a)
    {
        line = output[a].c_str();

        if (line == "??:0")
        {
            line.clear();
        }
        else
        {
            line = line.substr(line.rfind('/') + 1, line.length() - line.rfind('/') - 1);
        }

        lines.push_back(line);
    }

    for (int a=0; a<functions.size(); ++a)
    {
        cerr << "[" << a << "] " <<  functions[a];
        if (!lines[a].empty())
        {
            cerr << " | " << lines[a] << endl;
        }
        else 
        {
            cerr << " | " << addresses[a] << endl;
        }    
    }

#endif
}

#define wxGTK_WINDOW 1
#define SHIFT (8*(sizeof(short int)-sizeof(char)))

static bool GetColourWidget(int & red, int & green, int & blue, int type)
{
    GtkWidget * widget;
    switch (type)
    {
    case wxGTK_WINDOW:
        widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        break;
    default:
        return FALSE;
    }
    GtkStyle * def = gtk_rc_get_style(widget);
    bool ok = false;
    if (!def)
    {
        def = gtk_widget_get_default_style();
    }
    if (def)
    {
        GdkColor * col;
        col = def->bg;
        red = col[GTK_STATE_NORMAL].red;
        green = col[GTK_STATE_NORMAL].green;
        blue = col[GTK_STATE_NORMAL].blue;
        ok = true;
    }
    gtk_widget_destroy(widget);
    return ok;
}

// external helper function
wxColour GetColour(wxSystemColour what)
{
    static wxColour * _systemWindowColour = NULL;
    switch (what)
    {
    case wxSYS_COLOUR_WINDOW:
        if (!_systemWindowColour)
        {
            int red, green, blue;
            if (!GetColourWidget(red, green, blue, wxGTK_WINDOW))
            {
                red = green = blue = 0x9c40;
            }
            _systemWindowColour = new wxColour(red >> SHIFT, green >> SHIFT, blue >> SHIFT);
        }
        return * _systemWindowColour;
    }
}

void CxmuleApp::LoadCompatiblePrefs()
{
// Send newprefs event
    wxNotifyEvent event(wxEVT_PREFS_CHANGED);
    xmuledlg->flowchartwnd->AddPendingEvent(event);
}

void CxmuleApp::SetCompatiblePrefs()
{
    theApp.xmuledlg->statisticswnd->ShowInterval();
}
