/***************************************************************************
                          dchubsearch.cpp  -  description
                             -------------------
    begin                : Fri Mar 15 2002
    copyright            : (C) 2002-2005 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "dchubsearch.h"

/** Qt (TM) includes */
#include <QApplication>
#include <QLineEdit>
#include <QTextEdit>
#include <QRadioButton>
#include <QCheckBox>
#include <QLCDNumber>
#include <QComboBox>
#include <QSpinBox>
#include <QPushButton>
#include <QMessageBox>
#include <QStringList>
#include <QTabWidget>
#include <QCursor>
#include <QFileDialog>
#include <QUrl>
#include <QClipboard>
#include <QProgressBar>
//#include <qdragobject.h>
#include <QKeyEvent>
#include <QResizeEvent>
#include <QShowEvent>
#include <QEvent>
#include <QHeaderView>
#include <QFileInfo>
#include <QDateTime>
#include <QMdiArea>
#include <QByteArray>
#include <QLCDNumber>
#include <QHeaderView>
#include <QCompleter>

#include <set>

/** */
#include <dclib/dcos.h>
#include <dclib/cutils.h>
#include <dclib/core/cstringlist.h>

#ifndef WIN32
#include <stdlib.h>
#endif

#include "dcconfig.h"
#include "dcclient.h"
#include "dcconnectionmanager.h"
#include "dchublistmanager.h"
#include "dcfiletool.h"
#include "dctransferview.h"
#include "dcmenuhandler.h"
#include "dcwidget.h"
#include "dciconloader.h"
#include "dcguiutils.h"

#include "ui/DCDialogMagnet.h"

#include <dclib/core/cdir.h>
#include <dclib/core/cxml.h>
#include <dclib/chttp.h>

// for file type icons
#include <dclib/cfilemanager.h>

// for saving search results with correct encoding
#include <dclib/core/ciconv.h>

// for loading saved search results
#include <dclib/cmessagehandler.h>

// not in dchubsearch.h any more
#include <dclib/csearchmanager.h>

// all for checking a TTH is valid and so not send out useless searches
#include <dclib/core/cbytearray.h>
#include <dclib/core/cbase32.h>
#include <dclib/cfilehasher.h>

#include "search-result-columns.h"

//#define DEBUG_SEARCHMODEL

#ifdef DEBUG_SEARCHMODEL
#include <QtDebug>
#endif

QStringList *DCHubSearch::search_words = NULL;
DCHubSearch::QStringHash DCHubSearch::download_dirs;

/** */
DCHubSearch::DCHubSearch(QWidget * parent) : QWidget(parent) {
    //int idx;

    lastFileResultsWidth = -1;
    lastUserResultsWidth = -1;

    m_nFilteredResults = 0;

    setupUi(this);

    // set default icon
    setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiFIND));

    setAttribute(Qt::WA_DeleteOnClose);
    if (DCConnectionManager::Instance()->GetMdiArea()) {
        m_pContainerWindow = DCConnectionManager::Instance()->GetMdiArea()->addSubWindow(this);
        m_pContainerWindow->setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiFIND));
    } else {
        m_pContainerWindow = 0;
    }

    m_pSearchModel = new SearchListModel(this);

    treeView_SEARCHRESULT->setModel(m_pSearchModel);

    // select at least
    ComboBox_SEARCHLIMIT->setCurrentIndex(1);

    m_pMessageList = new CList<CDCMessage > ();
    m_pSearchResultList = new CList<CMessageSearchResult > ();
    m_pSearchQueryList = 0;
    m_pSearchHistory = new CList<CDCMessage > ();

    InitDocument();

    /*
     * CSearchManager does not delete the callback function anymore
     * because multiple search windows will never work with that.
     */
    m_pOurCallback = new CCallback1<DCHubSearch, CDCMessage*>(this, &DCHubSearch::DC_CallBack);


    /* figure out global search state */
    switch (CSearchManager::Instance()->SearchType()) {
        case estyNONE:
            m_eSearchState = egssREADY;
            Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
            SetSearchView(true);
            break;
        case estySINGLE:
        case estyMULTI:
            m_eSearchState = egssOTHER;
            Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));
            SetSearchView(false);
            break;
        case estyEXTERNAL:
            m_eSearchState = egssAUTO;
            Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));
            SetSearchView(false);
            break;
    }

    if (CSearchManager::Instance()->GetCallBackFunction() == NULL) {
        CSearchManager::Instance()->SetCallBackFunction(m_pOurCallback);
    }

    m_Timer.setSingleShot(true);
    m_Timer.start(500);
}

/** */
DCHubSearch::~DCHubSearch() {
    DeInitDocument();

    if (CSearchManager::Instance()->GetCallBackFunction() == m_pOurCallback) {
        if ((m_eSearchState == egssSEARCH) && ((CSearchManager::Instance()->SearchType() == estySINGLE) || (CSearchManager::Instance()->SearchType() == estyMULTI))) {
            CSearchManager::Instance()->StopSearch();
        }
        CSearchManager::Instance()->SetCallBackFunction(NULL);
    }

    delete m_pOurCallback;
    m_pOurCallback = 0;

    ClearSearchResults();

    Combobox_SEARCH->removeEventFilter(this);

    m_Timer.stop();

    SocketCallbackMutex.lock();

    // cleanup all lists
    delete m_pMessageList;
    m_pMessageList = NULL;

    delete m_pSearchResultList;
    m_pSearchResultList = NULL;

    delete m_pSearchQueryList;
    m_pSearchQueryList = NULL;

    delete m_pSearchHistory;
    m_pSearchHistory = NULL;

    SocketCallbackMutex.unlock();
}

/** */
void DCHubSearch::closeEvent(QCloseEvent *e) {
    emit onDie(this);

    m_pContainerWindow->close();

    setAttribute(Qt::WA_DeleteOnClose);

    e->accept();
}

/** */
void DCHubSearch::InitDocument() {
    StringMap * map;
    DCHubSearch::InitCompleterDict();

    // restore settings
    if (DCConfig::Instance()->GetMap("SEARCHVIEW", map)) {
        if (((*map)["WIDTH"].toInt() > 0) && ((*map)["HEIGHT"].toInt() > 0)) {
            if (m_pContainerWindow != 0) {
                m_pContainerWindow->resize((*map)["WIDTH"].toInt(), (*map)["HEIGHT"].toInt());
            }
        }

        if (!((*map)["MAXTHREADS"].isEmpty())) {
            SpinBox_MAXTHREADS->setValue((*map)["MAXTHREADS"].toInt());
        }

        int column = -1;
        Qt::SortOrder order = Qt::AscendingOrder;

        if (map->contains("SORTCOLUMN"))
            column = map->value("SORTCOLUMN").toInt();

        if (map->contains("SORTORDER"))
            order = DCGuiUtils::SortOrderFromName(map->value("SORTORDER"));

        ((SearchListModel*)treeView_SEARCHRESULT->model())->sort(column, order);
    }

    if (DCConfig::Instance()->GetMap("SEARCHLISTCOLUMNORDER", map)) {
        treeView_SEARCHRESULT->header()->restoreState(QByteArray::fromBase64(map->value(QString("SEARCHLISTHEADERSTATE")).toAscii()));

        SizeColumnsPreservingRatios();
    }

    //treeView_SEARCHRESULT->header()->setMovable(true);

    LCDNumber_RESULTS->setSegmentStyle(QLCDNumber::Flat);
    treeView_SEARCHRESULT->header()->setContextMenuPolicy(Qt::CustomContextMenu);
    treeView_SEARCHRESULT->setAlternatingRowColors(DCConfig::Instance()->GetAlternatingRowColors());

    QStringList list(*DCHubSearch::GetCompleterDict());
    QCompleter *cmp = new QCompleter(list, Combobox_SEARCH);

    cmp->setCaseSensitivity(Qt::CaseInsensitive);
    cmp->setCompletionMode(QCompleter::InlineCompletion);

    lineEdit_SEARCHSTR->setCompleter(cmp);
    Combobox_SEARCH->addItems(list);

    connect(PushButton_SEARCH, SIGNAL(clicked()), this, SLOT(slotSearchReturnPressed()));
    connect(PushButton_ADDQUEUE, SIGNAL(clicked()), this, SLOT(slotAddSearchQueue()));
    connect(PushButton_REFRESHCONNECTEDHUBS, SIGNAL(clicked()), this, SLOT(slotRefreshConnectedHubs()));
    connect(PushButton_RESETPARAMS, SIGNAL(clicked()), this, SLOT(slotReset()));
    connect(treeView_SEARCHRESULT, SIGNAL(doubleClicked(const QModelIndex &)),
            this, SLOT(slotDoubleClickedSearchResult(const QModelIndex &)));
    connect(treeView_SEARCHRESULT, SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(slotRightButtonClickedSearchResult(const QPoint &)));
    connect(treeView_SEARCHRESULT->header(), SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(slotContextMenuHeader(const QPoint &)));

    //connect(Combobox_SEARCH, SIGNAL(activated(int)), this, SLOT(slotSearchSelected(int)));

    connect(PushButton_PURGEHISTORY, SIGNAL(clicked()), this, SLOT(slotPurgeHistory()));

    connect(ComboBox_SEARCHLIMIT, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSizeLimitChanged(int)));

    connect(&m_Timer, SIGNAL(timeout()), this, SLOT(timerDone()));

    //connect(Combobox_SEARCH, SLOT(clicked()), this, SLOT(slotClickedHistory()));

    lineEdit_SEARCHSTR->installEventFilter(this);
    Combobox_SEARCH->installEventFilter(this);

    ProgressBar_LOGHUBS->hide();

}

/** */
void DCHubSearch::DeInitDocument() {
    StringMap * map;

    // save search view settings
    DCConfig::Instance()->GetMap("SEARCHVIEW", map);

    if (m_pContainerWindow != 0) {
        (*map)["WIDTH"] = QString().setNum(m_pContainerWindow->width());
        (*map)["HEIGHT"] = QString().setNum(m_pContainerWindow->height());
    }
    (*map)["MAXTHREADS"] = QString().setNum(SpinBox_MAXTHREADS->value());

    (*map)["SORTCOLUMN"] = QString::number(((SearchListModel*)treeView_SEARCHRESULT->model())->getSortColumn());
    (*map)["SORTORDER"] = DCGuiUtils::SortOrderName(((SearchListModel*)treeView_SEARCHRESULT->model())->getSortOrder());

    saveColumns();

    DCConfig::Instance()->GetMap("SEARCHVIEWCOLUMNS", map);

    QTreeView *tree = treeView_SEARCHRESULT;

    (*map)["COLUMN_FILENAME"]  = QString().setNum(tree->columnWidth(COLUMN_FILENAME));
    (*map)["COLUMN_ALLSLOTS"]  = QString().setNum(tree->columnWidth(COLUMN_ALLSLOTS));
    (*map)["COLUMN_COUNT"]     = QString().setNum(tree->columnWidth(COLUMN_COUNT));
    (*map)["COLUMN_ESIZE"]     = QString().setNum(tree->columnWidth(COLUMN_ESIZE));
    (*map)["COLUMN_EXTENSION"] = QString().setNum(tree->columnWidth(COLUMN_EXTENSION));
    (*map)["COLUMN_FREESLOTS"] = QString().setNum(tree->columnWidth(COLUMN_FREESLOTS));
    (*map)["COLUMN_HOST"]      = QString().setNum(tree->columnWidth(COLUMN_HOST));
    (*map)["COLUMN_HUB"]       = QString().setNum(tree->columnWidth(COLUMN_HUB));
    (*map)["COLUMN_IP"]        = QString().setNum(tree->columnWidth(COLUMN_IP));
    (*map)["COLUMN_NICK"]      = QString().setNum(tree->columnWidth(COLUMN_NICK));
    (*map)["COLUMN_PATH"]      = QString().setNum(tree->columnWidth(COLUMN_PATH));
    (*map)["COLUMN_SIZE"]      = QString().setNum(tree->columnWidth(COLUMN_SIZE));
    (*map)["COLUMN_TTH"]       = QString().setNum(tree->columnWidth(COLUMN_TTH));

    if (lineEdit_SEARCHSTR->completer()){
        QCompleter *cmp = lineEdit_SEARCHSTR->completer();
        lineEdit_SEARCHSTR->setCompleter(NULL);

        delete cmp;
    }
}

/** overridden so that the column widths are initialized on first show() */
void DCHubSearch::showEvent(QShowEvent * event) {
    QWidget::showEvent(event);

    if (isVisible()) {
        SizeColumnsPreservingRatios();
    }
}

/** resize event handler */
void DCHubSearch::resizeEvent(QResizeEvent *e) {
    SizeColumnsPreservingRatios();

    //e->accept();
    QWidget::resizeEvent(e);
}

/** Initialize or adjust widths of the ListView columns */
void DCHubSearch::SizeColumnsPreservingRatios() {
    int width;

    if (treeView_SEARCHRESULT->isVisible()) {
        width = treeView_SEARCHRESULT->width();
        QTreeView *tree = treeView_SEARCHRESULT;
        StringMap *map;

        if (DCConfig::Instance()->GetMap("SEARCHVIEWCOLUMNS", map)) {
            if (lastFileResultsWidth == -1) {
                tree->setColumnWidth(COLUMN_FILENAME, (*map)["COLUMN_FILENAME"].toInt());
                tree->setColumnWidth(COLUMN_ALLSLOTS, (*map)["COLUMN_ALLSLOTS"].toInt());
                tree->setColumnWidth(COLUMN_COUNT, (*map)["COLUMN_COUNT"].toInt());
                tree->setColumnWidth(COLUMN_ESIZE, (*map)["COLUMN_ESIZE"].toInt());
                tree->setColumnWidth(COLUMN_EXTENSION, (*map)["COLUMN_EXTENSION"].toInt());
                tree->setColumnWidth(COLUMN_FREESLOTS, (*map)["COLUMN_FREESLOTS"].toInt());
                tree->setColumnWidth(COLUMN_HOST, (*map)["COLUMN_HOST"].toInt());
                tree->setColumnWidth(COLUMN_HUB, (*map)["COLUMN_HUB"].toInt());
                tree->setColumnWidth(COLUMN_IP, (*map)["COLUMN_IP"].toInt());
                tree->setColumnWidth(COLUMN_NICK, (*map)["COLUMN_NICK"].toInt());
                tree->setColumnWidth(COLUMN_PATH, (*map)["COLUMN_PATH"].toInt());
                tree->setColumnWidth(COLUMN_SIZE, (*map)["COLUMN_SIZE"].toInt());
                tree->setColumnWidth(COLUMN_TTH, (*map)["COLUMN_TTH"].toInt());

                lastFileResultsWidth = treeView_SEARCHRESULT->width();
            } else if (lastFileResultsWidth != width) {
                DCGuiUtils::AdjustColumnWidths(treeView_SEARCHRESULT, lastFileResultsWidth);
                lastFileResultsWidth = treeView_SEARCHRESULT->width();
            }
        }
        else if (width > 0) {
            if (lastFileResultsWidth == -1) {
                tree->setColumnWidth(RESULTS_COLUMN_FILE, ((width * 7) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_SIZE, ((width * 2) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_TTH, ((width * 6) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_PATH, ((width * 6) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_NICK, ((width * 3) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_FREESLOTS, ((width * 2) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_TOTALSLOTS, ((width * 2) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_IP, ((width * 3) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_HUB, ((width * 2) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_HOST, ((width * 2) / 30));
                tree->setColumnWidth(RESULTS_COLUMN_COUNT, ((width * 2) / 30));
                tree->setColumnWidth(COLUMN_ESIZE, ((width * 2) / 40));
                tree->setColumnWidth(COLUMN_EXTENSION, ((width * 2) / 40));

                lastFileResultsWidth = treeView_SEARCHRESULT->width();
            } else if (lastFileResultsWidth != width) {
                DCGuiUtils::AdjustColumnWidths(treeView_SEARCHRESULT, lastFileResultsWidth);
                lastFileResultsWidth = treeView_SEARCHRESULT->width();
            }
        }
    }
}

/** event filter */
bool DCHubSearch::eventFilter(QObject * object, QEvent * event) {
    if ((event->type() == QEvent::KeyPress) && ((QLineEdit*) object == lineEdit_SEARCHSTR)) {
        QKeyEvent * e = (QKeyEvent*) event;
        if ((e->key() == Qt::Key_Enter) || (e->key() == Qt::Key_Return)) {
            slotSearchReturnPressed();
        }
    }
    else if ((event->type() == QEvent::MouseButtonPress) && ((QComboBox*)object == Combobox_SEARCH)){
        slotClickedHistory();
    }

    return QWidget::eventFilter(object, event); // standard event processing
}

/** */
void DCHubSearch::slotSearchSelected(int sel) {
    CMessageSearchUser *msu;
    CMessageSearchFile *msf;
    int i;
    CDCMessage *hmsg = 0;

    i = 0;

    while ((hmsg = (CDCMessage *) m_pSearchHistory->Next((CDCMessage*) hmsg)) != 0) {
        if (i == sel)
            break;
        i++;
    }

    if (hmsg == 0)
        return;

    if (hmsg->m_eType == DC_MESSAGE_SEARCH_USER) {
        msu = (CMessageSearchUser *) hmsg;

        LineEdit_SEARCHSIZE->setText("0");
        ComboBox_SEARCHUNIT->setCurrentIndex(0);
        ComboBox_SEARCHLIMIT->setCurrentIndex(1);
        ComboBox_SEARCHTYPE->setCurrentIndex(0);
    } else {
        msf = (CMessageSearchFile *) hmsg;

        LineEdit_SEARCHSIZE->setText(QString().setNum(msf->m_nSize));
        ComboBox_SEARCHUNIT->setCurrentIndex(0);

        if (msf->m_bSizeLimit) {
            switch (msf->m_eSizeType) {
                case esstATLEAST:
                    ComboBox_SEARCHLIMIT->setCurrentIndex(1);
                    break;
                case esstATMOST:
                    ComboBox_SEARCHLIMIT->setCurrentIndex(2);
                    break;
                default:
                    break;
            }
        } else {
            ComboBox_SEARCHLIMIT->setCurrentIndex(0);
        }

        ComboBox_SEARCHTYPE->setCurrentIndex(msf->m_eFileType);
    }
}

/** */
void DCHubSearch::addHistory(CDCMessage * msg) {
    CMessageSearchUser *msu1, *msu2;
    CMessageSearchFile *msf1, *msf2;
    CString s1, s2;
    CDCMessage *hmsg = 0;
    bool found = false;

    if (!msg)
        msg = GetSearchObject();
    if (!msg)
        return;

    if (msg->m_eType == DC_MESSAGE_SEARCH_USER) {
        msu1 = (CMessageSearchUser *) msg;
        s1 = msu1->m_sNick;
    } else {
        msf1 = (CMessageSearchFile *) msg;
        s1 = msf1->m_sString;
    }

    while ((hmsg = (CDCMessage *) m_pSearchHistory->Next((CDCMessage*) hmsg)) != 0) {
        if (msg->m_eType == hmsg->m_eType) {
            if (msg->m_eType == DC_MESSAGE_SEARCH_USER) {
                msu2 = (CMessageSearchUser *) hmsg;
                s2 = msu2->m_sNick;
            } else {
                msf2 = (CMessageSearchFile *) hmsg;
                s2 = msf2->m_sString;
            }

            if (s1 == s2 ) {
                m_pSearchHistory->Del(hmsg);
                m_pSearchHistory->Add(msg);
                found = true;
                break;
            }
        }
    }

    if (!DCHubSearch::GetCompleterDict()->contains(QString::fromAscii(s1.Data()))){
        DCHubSearch::GetCompleterDict()->append(QString::fromAscii(s1.Data()));

        QStringList list(*DCHubSearch::GetCompleterDict());

        if (Combobox_SEARCH->completer()){
            QCompleter *cmp = lineEdit_SEARCHSTR->completer();
            lineEdit_SEARCHSTR->setCompleter(NULL);

            delete cmp;
        }

        QCompleter *cmp = new QCompleter(list, Combobox_SEARCH);
        cmp->setCaseSensitivity(Qt::CaseInsensitive);
        cmp->setCompletionMode(QCompleter::InlineCompletion);

        lineEdit_SEARCHSTR->setCompleter(cmp);
    }

    if (!found) {
        m_pSearchHistory->Add(msg);
    }
}

/** */
void DCHubSearch::SetSearchView(bool enabled) {
    if (enabled) {
        PushButton_SEARCH->setText(tr("Start"));
        PushButton_SEARCH->setEnabled(true);
    } else {
        PushButton_SEARCH->setText(tr("Stop"));
    }

    //LineEdit_SEARCH->setEnabled(enabled);
    Combobox_SEARCH->setEnabled(enabled);
    ButtonGroup_HUBS->setEnabled(enabled);
    SpinBox_MAXTHREADS->setEnabled(enabled);
    PushButton_ADDQUEUE->setEnabled(enabled);
    CheckBox_ENABLETAG->setEnabled(enabled);
    CheckBox_MULTISEARCH->setEnabled(enabled);
    PushButton_RESETPARAMS->setEnabled(enabled);
    PushButton_PURGEHISTORY->setEnabled(enabled);
    TextLabel_TYPE->setEnabled(enabled);
    ComboBox_SEARCHTYPE->setEnabled(enabled);
    ComboBox_SEARCHLIMIT->setEnabled(enabled);
    LineEdit_SEARCHSIZE->setEnabled(enabled);
    ComboBox_SEARCHUNIT->setEnabled(enabled);
    Label_MAX_RESULTS->setEnabled(enabled);
    SpinBox_MAXRESULT->setEnabled(enabled);
    lineEdit_SEARCHSTR->setEnabled(enabled);

#ifdef DEBUG_SEARCHMODEL
    const QList <SearchListItem*> &items = m_pSearchModel->getRowItems();

    for (int i = 0; i < items.size(); i++){
        QString result = "";
        for (int j = 0; j < COLUMN_PATH; j++)
            result += items.at(i)->data(j).toString() + "; ";

        qDebug() << result;
    }
#endif

}

/** */
int DCHubSearch::DC_CallBack(CDCMessage * DCMsg) {
    SocketCallbackMutex.lock();

    int err = -1;

    if ((DCMsg != 0) && (m_pMessageList != 0)) {
        m_pMessageList->Add(DCMsg);
        err = 0;
    }

    SocketCallbackMutex.unlock();

    return err;
}

/** */
void DCHubSearch::timerDone() {
    int i, t;

    if (m_eSearchState == egssREADY) {
        switch (CSearchManager::Instance()->SearchType()) {
            case estyNONE:
                break;
            case estySINGLE:
            case estyMULTI:
                if (CSearchManager::Instance()->GetCallBackFunction() != m_pOurCallback) {
                    m_eSearchState = egssOTHER;
                    Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));
                    SetSearchView(false);
                } else {
                    Label_GLOBAL_SEARCH_STATUS->setText(tr("Search..."));
                    m_eSearchState = egssSEARCH;
                    SetSearchView(false);
                }
                break;
            case estyEXTERNAL:
                m_eSearchState = egssAUTO;
                Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));
                SetSearchView(false);

                /* show log message */
                ShowResults(false);

                break;
        }
    } else if (m_eSearchState == egssOTHER) {
        switch (CSearchManager::Instance()->SearchType()) {
            case estyNONE:
                m_eSearchState = egssREADY;
                Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
                SetSearchView(true);
                break;
            case estySINGLE:
            case estyMULTI:
                /* other search is still running */
                break;
            case estyEXTERNAL:
                m_eSearchState = egssAUTO;
                Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));
                SetSearchView(false);
                break;
        }
    } else if (m_eSearchState == egssAUTO) {
        switch (CSearchManager::Instance()->SearchType()) {
            case estyNONE:
                /* show any more log messages */
                ShowResults(false);

                m_eSearchState = egssREADY;
                Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
                SetSearchView(true);

                break;
            case estySINGLE:
            case estyMULTI:
                if (CSearchManager::Instance()->GetCallBackFunction() != m_pOurCallback) {
                    m_eSearchState = egssOTHER;
                    Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));
                    SetSearchView(false);
                } else {
                    Label_GLOBAL_SEARCH_STATUS->setText(tr("Search..."));
                    m_eSearchState = egssSEARCH;
                    SetSearchView(false);
                }

                break;
            case estyEXTERNAL:
                /* auto search still running */
                /* show any more log messages */
                ShowResults(false);

                break;
        }
    } else if (m_eSearchState == egssSEARCH) {
        ShowResults(false);
        qApp->processEvents();

        switch (CSearchManager::Instance()->SearchType()) {
            case estyNONE:
                m_eSearchState = egssREADY;
                Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
                SetSearchView(true);
                setWindowTitle(tr("%1 - %2 Results").arg(GetSearchQueryString()).arg(m_pSearchResultList->Count()));

                break;
            case estySINGLE:
            case estyMULTI:
                if (CSearchManager::Instance()->GetCallBackFunction() == m_pOurCallback) {
                    i = 0;
                    if (CSearchManager::Instance()->HubCount() > 0)
                        i = ((CSearchManager::Instance()->HubIndex()*100) / CSearchManager::Instance()->HubCount());
                    ProgressBar_LOGHUBS->setValue(i);

                    i = time(0) - CSearchManager::Instance()->StartTime();

                    if (CSearchManager::Instance()->HubIndex() > 0)
                        t = ((i * CSearchManager::Instance()->HubCount()) / CSearchManager::Instance()->HubIndex());
                    else
                        t = 0;
                } else {
                    m_eSearchState = egssOTHER;
                    Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));

                    setWindowTitle(tr("%1 - %2 Results").arg(GetSearchQueryString()).arg(m_pSearchResultList->Count()));
                }

                break;
            case estyEXTERNAL:
                m_eSearchState = egssAUTO;
                Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));

                setWindowTitle(tr("%1 - %2 Results").arg(GetSearchQueryString()).arg(m_pSearchResultList->Count()));

                break;
        }
    }

    // restart timer
    m_Timer.setSingleShot(true);
    m_Timer.start(500);
}

void DCHubSearch::slotClickedHistory(){
    if (!DCHubSearch::GetCompleterDict())
        return;

    QStringList list(*DCHubSearch::GetCompleterDict());

    if (list.empty())
        return;

    QMenu *m = new QMenu(this);

    for (int i = 0; i < list.size(); i++)
        m->addAction(new QAction(list.at(i), m));

    QAction *ch = m->exec(QCursor::pos());

    if (!ch){
        delete m;
        return;
    }

    lineEdit_SEARCHSTR->setText(ch->text());
    lineEdit_SEARCHSTR->setFocus();
    lineEdit_SEARCHSTR->selectAll();

    delete m;
}

/** */
void DCHubSearch::ShowResults(bool bClearList) {
    CDCMessage *DCMsg;
    bool bupdate;

    if (bClearList) {
        ClearSearchResults();
    }

    if (SocketCallbackMutex.tryLock() == false) {
        return;
    }

    bupdate = false;

    if (m_pMessageList != 0) {
        while ((DCMsg = m_pMessageList->Next(0)) != 0) {
            m_pMessageList->Remove(DCMsg);

            switch (DCMsg->m_eType) {
                case DC_MESSAGE_SEARCHRESULT:
                {
                    bool b;
                    b = DC_SearchResult((CMessageSearchResult *) DCMsg);

                    if (b == true) {
                        DCMsg = 0;
                        bupdate = true;
                        treeView_SEARCHRESULT->setUpdatesEnabled(false);
                    }

                    break;
                }

                default:
                {
                    break;
                }
            }

            if (DCMsg)
                delete DCMsg;
        }
    }

    SocketCallbackMutex.unlock();

    if (bupdate) {
        treeView_SEARCHRESULT->setUpdatesEnabled(true);
    }
}

/** search result */
bool DCHubSearch::DC_SearchResult(CMessageSearchResult * MessageSearchResult) {
    bool res = false;

    if (CSearchManager::Instance()->SearchType() == estyNONE) {
        return res;
    }

    if (SpinBox_MAXRESULT->value() > 0) {
        if (m_pSearchResultList->Count() >= SpinBox_MAXRESULT->value()) {
            ++m_nFilteredResults;
            return res;
        }
    }

    /* SRs will have already been adjusted by CClient if in passive mode */
    if (DCConfig::Instance()->GetAdjustSearchResultHubNames() &&
            (DCConfig::Instance()->GetMode() == ecmACTIVE)) {
        if (DCConnectionManager::Instance()->GetConnectedHubCount() == 1) {
            if (RadioButton_CONNECTEDHUBS->isChecked() ||
                    RadioButton_CONNECTEDSINGLEHUB->isChecked()) {
                std::map<CString, CString> * hubmap = DCConnectionManager::Instance()->GetConnectedHubServerMap();

                if (hubmap != 0) {
                    std::map<CString, CString>::const_iterator it = hubmap->begin();
                    if (it != hubmap->end()) {
                        MessageSearchResult->m_sHubName = it->first;
                        MessageSearchResult->m_sHubHost = it->second;
                        // printf( "Adjusted hub name to %s\n", it->first.Data() );
                        // printf( "Adjusted hub host to %s\n", it->second.Data() );
                    }

                    delete hubmap;
                }
            }
        } else {
            if (RadioButton_CONNECTEDSINGLEHUB->isChecked()) {
                QString both = ComboBox_CONNECTEDHUBS->currentText();
                if (!both.isEmpty()) {
                    MessageSearchResult->m_sHubName = both.left(both.lastIndexOf('(')).toAscii().constData();
                    int start = both.lastIndexOf('(') + 1;
                    MessageSearchResult->m_sHubHost = both.mid(start, both.length() - (start + 1)).toAscii().constData();
                }
            }
        }
    }

    ShowSearchResult(MessageSearchResult);

    res = true;

    m_pSearchResultList->Add(MessageSearchResult);

    LCDNumber_RESULTS->display((int) m_pSearchResultList->Count());

    return res;
}

/** */
void DCHubSearch::ShowSearchResult(CMessageSearchResult * MessageSearchResult) {
    static int hog_counter = 0;

    eFileTypes type = CFileManager::Instance()->GetFileType(MessageSearchResult->m_sFile);
    QPixmap icon = QPixmap();

    switch (type) {
        case eftMP3:
            icon = DCIconLoader::Instance()->GetPixmap(eiFILETYPE_MP3);
            break;
        case eftARCHIVE:
            icon = DCIconLoader::Instance()->GetPixmap(eiFILETYPE_ARCHIVE);
            break;
        case eftDOCUMENT:
            icon = DCIconLoader::Instance()->GetPixmap(eiFILETYPE_DOCUMENT);
            break;
        case eftAPPLICATION:
            icon = DCIconLoader::Instance()->GetPixmap(eiFILETYPE_APPLICATION);
            break;
        case eftPICTURE:
            icon = DCIconLoader::Instance()->GetPixmap(eiFILETYPE_PICTURE);
            break;
        case eftVIDEO:
            icon = DCIconLoader::Instance()->GetPixmap(eiFILETYPE_VIDEO);
            break;
        default:
            icon = DCIconLoader::Instance()->GetPixmap(eiFILETYPE_UNKNOWN);
            break;
    }

    if (MessageSearchResult->m_nSize <= 0)
        icon = DCIconLoader::Instance()->GetPixmap(eiFOLDER_BLUE);

    CString spath, sname;

    CDir().SplitPathFile(MessageSearchResult->m_sFile, spath, sname);

    std::set<unsigned long> *my_set = CFileManager::Instance()->SearchHash(MessageSearchResult->m_sHash.Data());

    bool exists = false;

    if (my_set && my_set->size() > 0){
        exists = true;

        delete my_set;
    }

    ((SearchListModel*)treeView_SEARCHRESULT->model())->addResult(sname.Data(),
                                                                  icon,
                                                                  MessageSearchResult->m_nSize,
                                                                  MessageSearchResult->m_sHash.Data(),
                                                                  spath.Data(),
                                                                  MessageSearchResult->m_sNick.Data(),
                                                                  MessageSearchResult->m_nFreeSlot,
                                                                  MessageSearchResult->m_nMaxSlot,
                                                                  MessageSearchResult->m_sSrcIP.Data(),
                                                                  MessageSearchResult->m_sHubName.Data(),
                                                                  MessageSearchResult->m_sHubHost.Data(),
                                                                  exists);

    hog_counter++;

    if (hog_counter == 60){
        qApp->processEvents();
        hog_counter = 0;
    }
}

QString DCHubSearch::GetSearchQueryString() {
    return lineEdit_SEARCHSTR->text();
}

/** */
CDCMessage * DCHubSearch::GetSearchObject() {
    CMessageSearchUser * MessageSearchUser = 0;
    CMessageSearchFile * MessageSearchFile = 0;

    if (lineEdit_SEARCHSTR->text().isEmpty()) {
        return 0;
    }

    // set searchmode
    if (ComboBox_SEARCHTYPE->currentIndex() == 0) {
        MessageSearchUser = new CMessageSearchUser();
        MessageSearchUser->m_sNick = lineEdit_SEARCHSTR->text().simplified().toAscii().constData();

        return MessageSearchUser;
    }

    MessageSearchFile = new CMessageSearchFile();

    MessageSearchFile->m_eFileType = eFileTypes(ComboBox_SEARCHTYPE->currentIndex());

    MessageSearchFile->m_sString = lineEdit_SEARCHSTR->text().simplified().toAscii().constData();

    if (MessageSearchFile->m_eFileType == eftHASH) {
        // fix tth
        MessageSearchFile->m_sString = MessageSearchFile->m_sString.ToUpper();

        // cannot check TTH valid here because it comes up twice
    }

    MessageSearchFile->m_bLocal = (DCConfig::Instance()->GetMode() == ecmPASSIVE);
    /* CClient handles setting source to the correct nick or IP */

    MessageSearchFile->m_nSize = LineEdit_SEARCHSIZE->text().toULongLong();

    if (ComboBox_SEARCHUNIT->currentIndex() == 1) {
        MessageSearchFile->m_nSize *= 1024;
    } else if (ComboBox_SEARCHUNIT->currentIndex() == 2) {
        MessageSearchFile->m_nSize *= 1024 * 1024;
    } else if (ComboBox_SEARCHUNIT->currentIndex() == 3) {
        MessageSearchFile->m_nSize *= 1024 * 1024 * 1024;
    }

    if (ComboBox_SEARCHLIMIT->currentIndex() == 1) {
        /* disable size limit if search is "at least 0 bytes" */
        MessageSearchFile->m_bSizeLimit = (MessageSearchFile->m_nSize > 0);

        MessageSearchFile->m_eSizeType = esstATLEAST;
    } else if (ComboBox_SEARCHLIMIT->currentIndex() == 2) {
        MessageSearchFile->m_bSizeLimit = true;
        MessageSearchFile->m_eSizeType = esstATMOST;
    } else if (ComboBox_SEARCHLIMIT->currentIndex() == 0) {
        MessageSearchFile->m_bSizeLimit = false;
        MessageSearchFile->m_eSizeType = esstATLEAST;
    }

    return MessageSearchFile;
}

/** */
void DCHubSearch::slotRefreshConnectedHubs() {
    ComboBox_CONNECTEDHUBS->clear();

    std::map<CString, CString> * hubmap = DCConnectionManager::Instance()->GetConnectedHubServerMap();
    if (hubmap != 0) {
        for (std::map<CString, CString>::const_iterator it = hubmap->begin(); it != hubmap->end(); ++it) {
            // add hubname with hubhost
            ComboBox_CONNECTEDHUBS->addItem(QString::fromAscii((it->first + " (" + it->second + ")").Data()));
        }

        delete hubmap;
    }
}

/** */
void DCHubSearch::slotSearchReturnPressed() {
    if (m_eSearchState == egssREADY) {
        startSearch();
    } else {
        CSearchManager::Instance()->StopSearch();
        PushButton_SEARCH->setEnabled(false);
    }
}

/** */
void DCHubSearch::startSearch() {
    CDCMessage * DCMessage;
    CStringList<CString> * hublist = 0;

    eSearchMode searchmode;
    eSearchType searchtype;

    if (CSearchManager::Instance()->SearchType() != estyNONE) {
        QMessageBox::critical(this, tr("Hub Search Error"),
                tr("Another search is already running!"));
        return;
    }

    // clear messagelist
    SocketCallbackMutex.lock();

    /* FIXME why not just clear the list? CList has auto-delete */
    while ((DCMessage = m_pMessageList->Next(0)) != 0) {
        m_pMessageList->Del(DCMessage);
    }

    m_pSearchResultList->Clear();

    SocketCallbackMutex.unlock();

    // clear searchresults
    ClearSearchResults();

    // reset counter
    LCDNumber_RESULTS->display(0);
    ProgressBar_LOGHUBS->setMinimum(0);
    ProgressBar_LOGHUBS->setMaximum(100);
    ProgressBar_LOGHUBS->setValue(0);
    m_nFilteredResults = 0;

    // clear the list on new search if only one search in the list
    if ((m_pSearchQueryList != 0) && (m_pSearchQueryList->Count() == 1)) {
        m_pSearchQueryList->Clear();
    }

    // add a new search into empty list
    if ((m_pSearchQueryList == 0) || (m_pSearchQueryList->Count() == 0)) {
        slotAddSearchQueue();
    }

    addHistory();

    if ((m_pSearchQueryList == 0) || (m_pSearchQueryList->Count() == 0)) {
        QMessageBox::critical(this, tr("Hub Search Error"),
                tr("Please add a search!"));
        return;
    }

    // single or multi search
    if (CheckBox_MULTISEARCH->isChecked()) {
        searchtype = estyMULTI;
    } else {
        searchtype = estySINGLE;
    }

    if ((DCConfig::Instance()->GetMode() == ecmPASSIVE) && (searchtype == estyMULTI)) {
        QMessageBox::critical(this, tr("Hub Search Error"),
                tr("Multi Search only work in active mode!"));
        return;
    }

    if (CSearchManager::Instance()->GetCallBackFunction() != m_pOurCallback) {
        CSearchManager::Instance()->SetCallBackFunction(m_pOurCallback);
    }

    // set searchmode
    if (RadioButton_CONNECTEDSINGLEHUB->isChecked()) {
        searchmode = esmCONNECTEDSINGLE;
    } else if (RadioButton_CONNECTEDHUBS->isChecked()) {
        searchmode = esmCONNECTEDALL;
    } else if (RadioButton_FILTEREDHUBS->isChecked()) {
        hublist = DCHubListManager::Instance()->GetFilteredHubList();

        searchmode = esmPUBLIC;
    } else if (RadioButton_AVAILABLEHUBS->isChecked()) {
        searchmode = esmPUBLIC;
    } else {
        searchmode = esmBOOKMARK;
    }

    // set max clients
    CSearchManager::Instance()->MaxClients(SpinBox_MAXTHREADS->value());
    // enable/disable tag
    CSearchManager::Instance()->EnableTag(CheckBox_ENABLETAG->isChecked());

    if (searchmode == esmCONNECTEDSINGLE) {
        CString hubname;

        // get current hubname
        hubname = ComboBox_CONNECTEDHUBS->currentText().toAscii().constData();
        // extract hubhost
        hubname = hubname.Mid(hubname.FindRev('(') + 1, hubname.Length() - hubname.FindRev('(') - 2);

        // check for empty hubhost
        if (hubname.IsEmpty()) {
            QMessageBox::critical(this, tr("Hub Search Error"),
                    tr("Please select a connected hub."));

            return;
        } else {
            // add hubhost to the hublist
            hublist = new CStringList<CString > ();
            hublist->Add(hubname, new CString(hubname));

            if (CSearchManager::Instance()->StartSearch(searchmode, searchtype, m_pSearchQueryList, hublist) != 0) {
                QMessageBox::critical(this, tr("Hub Search Error"),
                        tr("No connected hubs found."));

                return;
            }
        }
    } else if (searchmode == esmCONNECTEDALL) {
        if (CSearchManager::Instance()->StartSearch(searchmode, searchtype, m_pSearchQueryList, hublist) != 0) {
            QMessageBox::critical(this, tr("Hub Search Error"),
                    tr("No connected hubs found."));

            return;
        }
    } else {
        if (searchmode == esmPUBLIC) {
            QString whichhubs;
            if (hublist) {
                whichhubs = tr("%1 filtered public hubs").arg(hublist->Count());
            } else {
                whichhubs += tr("all public hubs");
            }

            if (CSearchManager::Instance()->StartSearch(searchmode, searchtype, m_pSearchQueryList, hublist) != 0) {
                delete hublist;

                QMessageBox::critical(this, tr("Hub Search Error"),
                        tr("No hubs found."));

                return;
            }
        } else {
            if (CSearchManager::Instance()->StartSearch(searchmode, searchtype, m_pSearchQueryList, hublist) != 0) {
                QMessageBox::critical(this, tr("Hub Search Error"),
                        tr("No hubs found."));

                return;
            }
        }
    }

    // update view
    SetSearchView(false);
    m_eSearchState = egssSEARCH;
    Label_GLOBAL_SEARCH_STATUS->setText(tr("Search..."));

    setWindowTitle(tr("%1 - Searching").arg(GetSearchQueryString()));

    return;
}

void DCHubSearch::raiseFocus(){
    if (isVisible()){
        lineEdit_SEARCHSTR->setFocus();
        lineEdit_SEARCHSTR->selectAll();
    }
}

/** */
void DCHubSearch::slotAddSearchQueue() {
    if (eFileTypes(ComboBox_SEARCHTYPE->currentIndex()) == eftHASH) {
        CString tth = lineEdit_SEARCHSTR->text().simplified().toUpper().toAscii().constData();
        CByteArray dst;
        if (CBase32::Decode(&dst, &tth) != CFileHasher::HashSize()) {
            if (QMessageBox::warning(
                    this,
                    tr("Add search"),
                    tr("Invalid TTH"),
                    QMessageBox::Ignore | QMessageBox::Cancel,
                    QMessageBox::Cancel) == QMessageBox::Cancel) {
                return;
            }
        }
    }

    CDCMessage * msg, *msg1;

    CMessageSearchUser * MessageSearchUser = 0;
    CMessageSearchFile * MessageSearchFile = 0;

    addHistory();

    if (m_pSearchQueryList == 0) {
        m_pSearchQueryList = new CList<CDCMessage > ();
    }

    if ((msg = GetSearchObject()) == 0) {
        return;
    }

    if (msg->m_eType == DC_MESSAGE_SEARCH_USER) {
        MessageSearchUser = (CMessageSearchUser *) msg;
    } else {
        MessageSearchFile = (CMessageSearchFile *) msg;
    }

    msg1 = 0;

    while ((msg1 = m_pSearchQueryList->Next(msg1)) != 0) {
        if (msg1->m_eType == msg->m_eType) {
            //TODO: compare if search already in the list
            if (MessageSearchUser) {
                if (((CMessageSearchUser*) msg1)->m_sNick == MessageSearchUser->m_sNick) {
                    return;
                }
            } else {
                if (((CMessageSearchFile*) msg1)->m_sString == MessageSearchFile->m_sString) {
                    return;
                }
            }
        }
    }

    m_pSearchQueryList->Add(msg);
}

/** */
void DCHubSearch::slotReset() {
    Combobox_SEARCH->clearEditText();
    LineEdit_SEARCHSIZE->setText("0");
    ComboBox_SEARCHUNIT->setCurrentIndex(0);
    ComboBox_SEARCHLIMIT->setCurrentIndex(1);
    ComboBox_SEARCHTYPE->setCurrentIndex(1);
    lineEdit_SEARCHSTR->setText("");

    if (m_pSearchQueryList != 0) {
        m_pSearchQueryList->Clear();
    }
}

/** */
void DCHubSearch::slotDoubleClickedSearchResult(const QModelIndex &index) {
    if (!index.isValid())
        return;

    qulonglong size = 0;
    qulonglong free_slots = 0;
    QString nick;
    QString hub;
    QString file;
    QString host;
    QString ip;
    QString path;
    QString tth;

    QModelIndexList mindexes = treeView_SEARCHRESULT->selectionModel()->selectedIndexes();
    QModelIndexList::const_iterator it;

    for (it = mindexes.constBegin(); it != mindexes.constEnd(); ++it){
        switch ((*it).column()){
            case COLUMN_ALLSLOTS:
                break;
            case COLUMN_FILENAME:
                file = (*it).data().toString();
                break;
            case COLUMN_FREESLOTS:
                free_slots = (*it).data().toULongLong();
                break;
            case COLUMN_HOST:
                host = (*it).data().toString();
                break;
            case COLUMN_HUB:
                hub = (*it).data().toString();
                break;
            case COLUMN_IP:
                ip = (*it).data().toString();
                break;
            case COLUMN_NICK:
                nick = (*it).data().toString();
                break;
            case COLUMN_PATH:
                path = (*it).data().toString();
                break;
            case COLUMN_ESIZE:
                size = (*it).data().toULongLong();
                break;
            case COLUMN_TTH:
                tth = (*it).data().toString();
            default:
                break;
        }
    }

    if (nick == "" || hub == "" || host == "" || ip == "" || file == "")
        return;

    if ((free_slots == 0)
        &&
        (QMessageBox::question( this, tr("Hub Search"), tr("This user has no free slots.\nContinue anyway?"),
                                   QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)){
        return;
    }

    if (size <= 0) {
        DownloadFolder(hub, host, path, file, nick);
    } else {
        // add file as new source
        DCFileTool::AddFileSource(this, nick.toAscii().constData(), hub.toAscii().constData(),
                                  host.toAscii().constData(), (path + file).toAscii().constData(),
                                  file.toAscii().constData(), CString(), CString(), eltFILE,
                                  size, tth.toAscii().constData());
    }

    return;
}

/** */
void DCHubSearch::slotRightButtonClickedSearchResult(const QPoint &) {
#ifdef DEBUG_SEARCHMODEL
    qDebug() << "slotRightButtonClickedSearchResult():\n";
#endif
    QModelIndexList mindexes = treeView_SEARCHRESULT->selectionModel()->selectedIndexes();

    if (mindexes.size() == 0){
#ifdef DEBUG_SEARCHMODEL
        qDebug() << "\tHas no selected indexes.";
#endif
        return;
    }

    SearchListItem *elem = (SearchListItem*)mindexes.at(0).internalPointer();//Selection mode: single item
    if (!elem){
#ifdef DEBUG_SEARCHMODEL
        qDebug() << "\tEmpty element.";
#endif
        return;
    }

#ifdef DEBUG_SEARCHMODEL
    qDebug() << "Item has " << elem->childCount() << " childs";
#endif

    QAction * chosen = 0;
    bool isadmin = false, allowDL = true;//, addUserCommands = true;
    CXml xml;
    //DCClient * client = 0;
    //QMap<QAction*, DC_UserMenuCommand*> addedcommands;

    QString file            = elem->data(COLUMN_FILENAME).toString();
    qulonglong free_slots   = elem->data(COLUMN_FREESLOTS).toULongLong();
    QString host            = elem->data(COLUMN_HOST).toString();
    QString hub             = elem->data(COLUMN_HUB).toString();
    QString nick            = elem->data(COLUMN_NICK).toString();
    QString path            = elem->data(COLUMN_PATH).toString();
    qulonglong size         = elem->data(COLUMN_ESIZE).toULongLong();
    QString tth             = elem->data(COLUMN_TTH).toString();

#ifdef DEBUG_SEARCHMODEL
    qDebug() << "\tFile: " << file;
    qDebug() << "\tFree slots: " << free_slots;
    qDebug() << "\tHost: " << host;
    qDebug() << "\tHub: " << hub;
    qDebug() << "\tNick: " << nick;
    qDebug() << "\tPath: " << path;
    qDebug() << "\tSize: " << size;
    qDebug() << "\tTTH: " << tth;
#endif

    if (nick == "" || hub == "" || host == "" || file == "")
        return;

    if (size <= 0)
        allowDL = false;

    if (DCConnectionManager::Instance()->IsAdmin(hub.toAscii().constData(), host.toAscii().constData()))
        isadmin = true;

    QMenu *m;

    m = new QMenu(this);

    QAction * dl = DCMenuHandler::addAction(m, emiDOWNLOAD, allowDL);
    QMenu * dl_to = DCMenuHandler::addMenu(m, emiDOWNLOAD_TO, allowDL, tr("Download to"));
    QAction * dl_as = DCMenuHandler::addAction(m, emiDOWNLOAD_AS, allowDL);
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * dl_dir = DCMenuHandler::addAction(m, emiDOWNLOAD_FOLDER, true);
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * repair = DCMenuHandler::addAction(m, emiDOWNLOAD_REPAIR, allowDL);
    QAction * binsec = DCMenuHandler::addAction(m, emiDOWNLOAD_BIN_SECTORS, allowDL);
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * dl_in = DCMenuHandler::addAction(m, emiDOWNLOAD_IN, allowDL);
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * browse = DCMenuHandler::addAction(m, emiBROWSE_USER_FILES, true);
    DCMenuHandler::addAction(m, emiSEPARATOR);
    //QAction * copycol = DCMenuHandler::addAction(m, emiCOPY_COLUMN_TO_CLIPBOARD, true);
    //QAction * copyrow = DCMenuHandler::addAction(m, emiCOPY_ROW_TO_CLIPBOARD, true);
    QAction * dclink = DCMenuHandler::addAction(m, emiCOPYDCFILELINK, true);
    QAction * magnet = DCMenuHandler::addAction(m, emiCOPYMAGNETLINK, true);
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * connhub = DCMenuHandler::addAction(m, emiCONNECT_TO_HUB, true);
    QAction * bookmark = DCMenuHandler::addAction(m, emiADD_BOOKMARK, true);
    DCMenuHandler::addAction(m, emiSEPARATOR);

    QList<DCDownloadAlias*> alias_list = DCConfig::Instance()->GetDownloadAliases();
    QList<QAction*> d_to_actions;

    for (int i = 0; i < alias_list.size(); i++){
	QAction *t = DCMenuHandler::addAction(dl_to, emiDOWNLOAD_FOLDER, true, alias_list.at(i)->alias);
	t->setToolTip(alias_list.at(i)->path);
	t->setText(alias_list.at(i)->alias);

	d_to_actions << t;
    }

    dl_to->addSeparator();

    if (!DCHubSearch::GetDownloadDirs().isEmpty()){
        QStringHash &hash = DCHubSearch::GetDownloadDirs();
        QHashIterator<QString, QString> it(hash);

        while (it.hasNext()){
            it.next();

            QAction *t = DCMenuHandler::addAction(dl_to, emiDOWNLOAD_FOLDER, true);
            t->setToolTip(it.key());
            t->setText(it.value());

            d_to_actions << t;
        }
    }

    QAction *dl_to_browse = DCMenuHandler::addAction(dl_to, emiDOWNLOAD_AS, true, tr("Browse"));
    dl_to_browse->setText(tr("Browse"));

    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * kick = DCMenuHandler::addAction(m, emiKICK, isadmin && true);
    QAction * forcemove = DCMenuHandler::addAction(m, emiFORCE_MOVE, isadmin && true);
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * load = DCMenuHandler::addAction(m, emiLOAD);
    QAction * save = DCMenuHandler::addAction(m, emiSAVE, (m_pSearchResultList->Count() > 0));

    chosen = m->exec(QCursor::pos());

    QString t_tip = "";

    if (d_to_actions.contains(chosen))
        t_tip = chosen->toolTip();

    delete m;

    if (chosen == 0) {
        return;
    }
    else if (d_to_actions.contains(chosen)){
	QString localrootpath = t_tip;
	QString localname;

        if ((free_slots == 0)
            &&
            (QMessageBox::question( this, tr("Hub Search"), tr("This user has no free slots.\nContinue anyway?"),
                                   QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)){
            return;
        }

        DCFileTool::CheckFile(this, nick.toAscii().constData(), hub.toAscii().constData(),
                                  host.toAscii().constData(),  (path + file).toAscii().constData(),
                                  file.toAscii().constData(), CString(), localrootpath.toAscii().constData(),
                                  eltFILE,  size, tth.toAscii().constData(), false, ertNONE);

	return;
    }
    else if (chosen == dclink) {
        CString s;

        s += "DCFILE://";
        s += xml.ToUTF8(host.toAscii().constData());
        s += "?file=";
        s += CHttp::Encode((path + file).toAscii().constData());
        s += "&size=";
        s += CString::number(size);
        s += "&nick=";
        s += CHttp::Encode(nick.toAscii().constData());
        s += "&hub=";
        s += CHttp::Encode(hub.toAscii().constData());

        if (!tth.isEmpty()) {
            s += "&hash=";
            s += CHttp::Encode(tth.toAscii().constData());
        }

        s += "\n";

        QApplication::clipboard()->setText(QString::fromAscii(s.Data()).trimmed());
    }
    else if (chosen == magnet) {
        //magnet:?xt=urn:tree:tiger:EOSA5AGTL5SD3VWCF3R2OH2WMGXV3S3R7SYN4YA&xl=708780032&dn=FC-6-i386-disc1.iso
        QString text;

        text += "magnet:?xt=urn:tree:tiger:";
        text += tth;

        // get size from myvalue used for numeric sorting
        text += "&xl=";
        text += QString().setNum(size);
        text += "&dn=";
        QString filename = QString(QUrl::toPercentEncoding(file));

        filename.replace("%20", "+");
        text += filename;

        text += "\n";

        text = text.trimmed();

        QApplication::clipboard()->setText(text);
    }
    else if ((chosen == dl) || (chosen == dl_as) || (chosen == dl_to_browse)
            || (chosen == repair) || (chosen == binsec)) {
        QString localrootpath;
        QString localname = file;

        // select downloadfolder for all selected files
	if (chosen == dl_to_browse) {
            localrootpath = QFileDialog::getExistingDirectory(this, tr("Select download folder"));

            if (localrootpath.isEmpty())
                return;

            QStringHash &hash = DCHubSearch::GetDownloadDirs();

            if (!hash.contains(localrootpath))
                hash.insert(localrootpath, QDir(localrootpath).dirName());
        }

        if ((chosen == dl_as) || (chosen == repair) || (chosen == binsec)) {
            QString save_root = DCConfig::Instance()->GetDownloadFinishedFolder().Data();

            if (save_root.isEmpty() || save_root.isNull())
                save_root = DCConfig::Instance()->GetDownloadFolder().Data();

            localrootpath = QFileDialog::getSaveFileName(this, tr("Select file for") + " " + file,
                                                         save_root+"/"+file, tr("All files (*.*)"));

            if (localrootpath.isEmpty())
                return;

            QFileInfo fi(localrootpath);
            localrootpath = fi.path();
            localname = fi.fileName();

            if ((localrootpath.isEmpty()) || (localname.isEmpty()))
                return;
        }
        // add transfer to the waitlist
        eRepairType repairtype;
        if (chosen == repair) {
            repairtype = ertBYTES;
        } else if (chosen == binsec) {
            repairtype = ertSECTORS;
        } else {
            repairtype = ertNONE;
        }
        
        if ((free_slots == 0)
            &&
            (QMessageBox::question( this, tr("Hub Search"), tr("This user has no free slots.\nContinue anyway?"),
                                   QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)){
            return;
        }

        DCFileTool::CheckFile(this, nick.toAscii().constData(), hub.toAscii().constData(), host.toAscii().constData(),
                    (path + file).toAscii().constData(), localname.toAscii().constData(), CString(), localrootpath.toAscii().constData(),
                    eltFILE, size, tth.toAscii().constData(), false, repairtype);

        // redraw the list
        treeView_SEARCHRESULT->repaint();
    }else if (chosen == dl_in) {
        QString localrootpath;
        QString localname;
        QString localpath;

        if (DCFileTool::SelectFileSource(this, size, tth, localname, localrootpath, localpath) == false) {
            return;
        }

        if ((free_slots == 0)
            &&
            (QMessageBox::question( this, tr("Hub Search"), tr("This user has no free slots.\nContinue anyway?"),
                                   QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)){
            return;
        }

        // add transfer to the waitlist
        DCFileTool::CheckFile(this, nick.toAscii().constData(), hub.toAscii().constData(), host.toAscii().constData(),
                    (path + file).toAscii().constData(), localname.toAscii().constData(), localpath.toAscii().constData(),
                    localrootpath.toAscii().constData(), eltFILE, size, tth.toAscii().constData(), true);

        // redraw the list
        treeView_SEARCHRESULT->repaint();
    } else if (chosen == browse) {

        CString empty;

        CString hubname(hub.toAscii().constData());
        CString hubhost(host.toAscii().constData());

        if (DCConnectionManager::Instance()->IsHubOnline(hubname, hubhost) == ehsNONE) {
            switch (QMessageBox::warning(this, tr("Filelist download"),
                        tr("Not connected to required hub!"),
                        tr("Connect"),
                        tr("Cancel"), 0, 0, 1))
            {
                    case 0:
                        DCConnectionManager::Instance()->Connect(hubname, hubhost);
                        break;
                    case 1:
                        break;
            }
        }

        DCTransferView::Instance()->DLM_QueueAdd(nick.toAscii().constData(), hubname, hubhost,
                    DC_USER_FILELIST, DC_USER_FILELIST, (path + file).toAscii().constData(),
                    empty,
                    eltBUFFER,
                    0, 0, 0, empty);
    }
    else if (chosen == dl_dir) {
        DownloadFolder(hub, host, path, file, nick);
    }
    /*else if (chosen == copycol) {
        QString s;

        for (int i = 0; i < selitems.size(); i++) {
            curitem = selitems.at(i);

            s += curitem->text(column);
            s += "\n";
        }

        s = s.trimmed();
        QApplication::clipboard()->setText(s);
    }
    else if (chosen == copyrow) {
        int idx;
        QString s;

        for (int i = 0; i < selitems.size(); i++) {
            curitem = selitems.at(i);

            for (idx = 0; idx < ListView_SEARCHRESULT->columns(); idx++) {
                s += curitem->text(idx);
                s += " ";
            }

            s += "\n";
        }

        s = s.trimmed();
        QApplication::clipboard()->setText(s);
    }*/
    else if (chosen == connhub) {
        // connect to the hub
        DCConnectionManager::Instance()->Connect(hub.toAscii().constData(), host.toAscii().constData());
    }
    else if (chosen == bookmark) {
        // bookmark this hub
        DCHubListManager::Instance()->AddBookmark(hub, host, QString());
    }
    else if (chosen == kick) {
        // TODO: need a patch to kick a user only one time
        QString message;

        if (!DCClient::GetOPKickMessage(message, this)) {
            return;
        }

        /** TODO: remove items from model!!!*/
        DCConnectionManager::Instance()->OPKick(hub, host, nick, message);
    }
    else if (chosen == forcemove) {
        // TODO: need a patch to move a user only one time
        QString message, _host;

        if (!DCClient::GetOPForceMoveMessage(message, _host, this)) {
            return;
        }
        // force move this user
        DCConnectionManager::Instance()->OPForceMove(hub, _host, nick, message, host);
    }
    else if (chosen == save) {
        QString filename = QFileDialog::getSaveFileName(
                this,
                tr("Choose a filename to save under"));

        if (!filename.isEmpty()) {
            QFile f(filename);

            if (f.open(QIODevice::WriteOnly)) {
                CIconv ciconv(DCConfig::Instance()->GetLocalEncoding(), DCConfig::Instance()->GetRemoteEncoding());
                CString s;
                CMessageSearchResult * msr = 0;

                while ((msr = m_pSearchResultList->Next(msr)) != 0) {
                    s = "$SR ";
                    s += ciconv.encode(msr->m_sNick);
                    s += " ";
                    s += ciconv.encode(msr->m_sFile);
                    if (msr->m_bFolder == false) {
                        s += 0x05;
                        s += CString::number(msr->m_nSize);
                    }
                    s += " ";
                    s += CString::number(msr->m_nFreeSlot);
                    s += "/";
                    s += CString::number(msr->m_nMaxSlot);
                    s += 0x05;
                    if (msr->m_sHash.IsEmpty()) {
                        s += ciconv.encode(msr->m_sHubName);
                    } else {
                        s += "TTH:";
                        s += msr->m_sHash;
                    }
                    s += " (";
                    s += msr->m_sHubHost;
                    s += ")|\n";
                    f.write(s.Data(), s.Length());
                }

                f.close();
            }
        }
    }
    else if (chosen == load) {
        QString filename = QFileDialog::getOpenFileName(
                this,
                tr("Choose a file to open"));

        if (!filename.isEmpty()) {
            QFile file(filename);
            if (file.open(QIODevice::ReadOnly)) {
                m_pSearchResultList->Clear();

                treeView_SEARCHRESULT->setUpdatesEnabled(false);
                ClearSearchResults();

                QByteArray buffer;
                buffer.resize(4096);

                CString m;
                int p;
                CDCMessage * dcmessage = 0;
                CMessageHandler MessageHandler;

                while (file.readLine(buffer.data(), buffer.size()) != -1) {
                    m = buffer.constData();
                    p = 0;

                    if (MessageHandler.Parse(&m, p, &dcmessage) == DC_MESSAGE_SEARCHRESULT) {
                        m_pSearchResultList->Add((CMessageSearchResult*) dcmessage);
                    }
                }

                file.close();

                treeView_SEARCHRESULT->setUpdatesEnabled(true);
                treeView_SEARCHRESULT->repaint();
                LCDNumber_RESULTS->display((int) m_pSearchResultList->Count());
            }
        }
    }
}

/** Clears the history of search strings */
void DCHubSearch::slotPurgeHistory() {
    Combobox_SEARCH->clear();

    slotReset();
    m_pSearchHistory->Clear();
    DCHubSearch::GetCompleterDict()->clear();

    if (Combobox_SEARCH->completer()){
        QCompleter *cmp = Combobox_SEARCH->completer();
        Combobox_SEARCH->setCompleter(NULL);

        delete cmp;
    }

    lineEdit_SEARCHSTR->setFocus();
    lineEdit_SEARCHSTR->selectAll();

    //TextEdit_LOG->clear();
}

/** Disable size entry if size limit not used */
void DCHubSearch::slotSizeLimitChanged(int index) {
    if (index == 0) {
        LineEdit_SEARCHSIZE->setEnabled(false);
        ComboBox_SEARCHUNIT->setEnabled(false);
    } else {
        LineEdit_SEARCHSIZE->setEnabled(true);
        ComboBox_SEARCHUNIT->setEnabled(true);
    }
}

/** */
void DCHubSearch::SetSearchForFile(QString file, eFileTypes filetype, int sizepos, ulonglong size) {
    lineEdit_SEARCHSTR->setText(file);

    ComboBox_SEARCHUNIT->setCurrentIndex(0);
    LineEdit_SEARCHSIZE->setText(QString().setNum(size));
    ComboBox_SEARCHLIMIT->setCurrentIndex(sizepos);

    ComboBox_SEARCHTYPE->setCurrentIndex(filetype);
}

/** */
void DCHubSearch::StartSearchWithPrompt() {
    if ((m_eSearchState == egssREADY) ||
            (QMessageBox::question(
            this,
            tr("Hub Search"),
            tr("Another search is running.\nStop other search?"),
            QMessageBox::Yes | QMessageBox::No,
            QMessageBox::No
            ) == QMessageBox::Yes)
            ) {
        slotSearchReturnPressed();
    }
}

/** */
void DCHubSearch::ClearSearchResults() {
    treeView_SEARCHRESULT->setUpdatesEnabled(false);
    m_pSearchModel->clearModel();
    treeView_SEARCHRESULT->setUpdatesEnabled(true);

    SizeColumnsPreservingRatios();
}

/** */
void DCHubSearch::DownloadFolder(const QString &hub, const QString &host,
                                 const QString &path, const QString &name,
                                 const QString &nick) {
    CString hubname(hub.toAscii().constData());
    CString hubhost(host.toAscii().constData());

    if (DCConnectionManager::Instance()->IsHubOnline(hubname, hubhost) == ehsNONE) {
        int ret = QMessageBox::warning(
                this,
                tr("Folder download"),
                tr("Not connected to required hub!"),
                tr("Connect"),
                tr("Cancel"),
                0,
                0,
                1
                );

        if (ret == 0) {
            DCConnectionManager::Instance()->Connect(hubname, hubhost);
        }
    }

    CString dir(path.toAscii().constData());
    dir += CString(name.toAscii().constData());

    DCTransferView::Instance()->DLM_QueueAdd(
            nick.toAscii().constData(),
            hubname,
            hubhost,
            DC_USER_FILELIST,
            DC_USER_FILELIST,
            dir,
            dir,
            eltBUFFER,
            0,
            0,
            0,
            CString()
            );
}

/** */
void DCHubSearch::slotContextMenuHeader(const QPoint & /* pos */) {
    QMenu * mcols = new QMenu(this);
    QAction * column;
    int index;

    for (int i = 0; i < m_pSearchModel->columnCount(); ++i) {
        index = treeView_SEARCHRESULT->header()->logicalIndex(i);
        column = mcols->addAction(m_pSearchModel->headerData(index, Qt::Horizontal).toString());
        column->setCheckable(true);
        if (index == 0) {
            column->setEnabled(false);
        }
        column->setChecked(treeView_SEARCHRESULT->header()->isSectionHidden(index) == false);
        column->setData(index);
    }

    mcols->addSeparator();
    QAction * save = DCMenuHandler::addAction(mcols, emiSAVE);
    QAction * chosen = mcols->exec(QCursor::pos());

    if (chosen == save) {
        saveColumns();
    } else if (chosen) {
        index = chosen->data().toInt();
        if (treeView_SEARCHRESULT->header()->isSectionHidden(index)) {
            treeView_SEARCHRESULT->header()->showSection(index);
        } else {
            treeView_SEARCHRESULT->header()->hideSection(index);
        }
    }

    delete mcols;
}

/** */
void DCHubSearch::saveColumns() {
    StringMap * map;
    DCConfig::Instance()->GetMap("SEARCHLISTCOLUMNORDER", map);
    (*map)["SEARCHLISTHEADERSTATE"] = QString(treeView_SEARCHRESULT->header()->saveState().toBase64().constData());
}

DCWaitMagnet *DCWaitMagnet::pWaitMagnet = NULL;

DCWaitMagnet::DCWaitMagnet(QWidget * parent) : QWidget(parent) {
    QString homepath = QDir::homePath();
    QString eis_path = homepath + "/.eiskaltdc";
    file = new QFile(QString(eis_path + "/eiskaltdc.magnet"));
    timerId = startTimer(2000);
}

DCWaitMagnet::~DCWaitMagnet() {
    killTimer(timerId);
    delete file;
}

void DCWaitMagnet::timerEvent(QTimerEvent *event) {
    if (event->timerId() == timerId) {
        testFile();
    } else {
        QWidget::timerEvent(event);
    }
}

void DCWaitMagnet::testFile() {
    if (file->exists()) {
        if (file->open(QIODevice::ReadOnly)) {
            magnet = file->readLine();
            file->close();
            file->remove();
            showMagnet(magnet);
        }
    }
}

void DCWaitMagnet::showMagnet(QString magnet) {

    QString key = QString::fromAscii("xt=urn:tree:tiger:");

    int i = magnet.indexOf(key);
    if (i == -1) {
        key = QString::fromAscii("xt.1=urn:tree:tiger:");
        i = magnet.indexOf(key);
    }

    int end = magnet.indexOf("&", i + 1);

    QString tth;
    if (i != -1) {
        i += key.length();
        if (end == -1) {
            tth = magnet.mid(i);
        } else {
            tth = magnet.mid(i, end - i);
        }
    }

    DCGuiApp::Instance()->WakeUp();

    switch (DCConfig::Instance()->GetMagnetAction(eChatMagnet)) {
        case eMagnetPrompt:
        {
            QString dispname;
            QDialog * dialog = new QDialog(this);
            Ui::DCDialogMagnet ui;
            ui.setupUi(dialog);

            ui.LineEdit_LINK->setText(magnet);
            ui.LineEdit_TTH->setText(tth);

            key = QString::fromAscii("dn=");
            i = magnet.indexOf(key);
            if (i != -1) {
                end = magnet.indexOf("&", i + 1);
                i += key.length();
                if (end == -1) {
                    dispname = magnet.mid(i);
                } else {
                    dispname = magnet.mid(i, end - i);
                }

                dispname.replace("+", "%20");
                dispname = QUrl::fromPercentEncoding(dispname.toUtf8());

                ui.LineEdit_NAME->setText(dispname);
            }

            key = QString::fromAscii("xl=");
            i = magnet.indexOf(key);
            if (i != -1) {
                end = magnet.indexOf("&", i + 1);
                i += key.length();
                ulonglong size;
                if (end == -1) {
                    size = magnet.mid(i).toULongLong();
                } else {
                    size = magnet.mid(i, end - i).toULongLong();
                }

                ui.LineEdit_SIZE->setText(DCGuiUtils::GetSizeString(size));
                ui.LineEdit_EXACT_SIZE->setText(QString::number(size));
            }

            if (dialog->exec() == QDialog::Accepted) {
                if (ui.CheckBox_SET_DEFAULT->isChecked()) {
                    if (ui.RadioButton_SEARCH->isChecked()) {
                        DCConfig::Instance()->SetMagnetAction(eMagnetSearch, eChatMagnet);
                    } else if (ui.RadioButton_DO_NOTHING->isChecked()) {
                        DCConfig::Instance()->SetMagnetAction(eMagnetNothing, eChatMagnet);
                    }
                }

                if (ui.RadioButton_SEARCH->isChecked()) {
                    DCHubSearch * hubsearch = new DCHubSearch(DCGuiApp::MdiAreaInstance());
                    hubsearch->SetSearchForFile(tth, eftHASH);
                    hubsearch->show();

                    connect(hubsearch, SIGNAL(onDie(QWidget*)), DCGuiApp::Instance(), SLOT(slotChildOnDie(QWidget*)));
                    DCConnectionManager::Instance()->addTab(hubsearch, tr("Hub search") + tth);

                    hubsearch->StartSearchWithPrompt();
                }
            }

            delete dialog;

            break;
        }
        case eMagnetSearch:
        {
            DCHubSearch * hubsearch = new DCHubSearch(DCGuiApp::MdiAreaInstance());
            hubsearch->SetSearchForFile(tth, eftHASH);
            hubsearch->show();

            connect(hubsearch, SIGNAL(onDie(QWidget*)), DCGuiApp::Instance(), SLOT(slotChildOnDie(QWidget*)));
            DCConnectionManager::Instance()->addTab(hubsearch, tr("Hub search") + tth);

            hubsearch->StartSearchWithPrompt();
            break;
        }
        case eMagnetDownload:
            /* not supported */
            break;
        case eMagnetNothing:
            break;
        default:
            break;
    }
}
