/***************************************************************************
 *   Copyright (C) 1999-2001 by Matthias Hoelzer-Kluepfel                  *
 *   hoelzer@kde.org                                                       *
 *   Copyright (C) 2001 by Bernd Gehrmann                                  *
 *   bernd@kdevelop.org                                                    *
 *   Copyright (C) 2004 by Alexander Dymo                                  *
 *   cloudtemple@mksat.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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/
#include "searchview.h"

#include <tqlayout.h>
#include <tqlabel.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqdir.h>
#include <tqregexp.h>

#include <kpushbutton.h>
#include <tdelistview.h>
#include <klineedit.h>
#include <kcombobox.h>
#include <tdelocale.h>
#include <kdialog.h>
#include <kprocess.h>
#include <tdeapplication.h>
#include <kstandarddirs.h>
#include <tdeconfig.h>
#include <tdemessagebox.h>
#include <kdebug.h>

#include <kdevpartcontroller.h>
#include <kdevdocumentationplugin.h>

#include "documentation_part.h"
#include "docutils.h"

SearchView::SearchView(DocumentationPart *part, TQWidget *parent, const char *name)
    :TQWidget(parent, name), m_part(part)
{
    TQVBoxLayout *l = new TQVBoxLayout(this, 0, KDialog::spacingHint());

    TQVBoxLayout *l2 = new TQVBoxLayout(l, 0);
    TQLabel *editLabel = new TQLabel(i18n("Wor&ds to search:"), this);
    l2->addWidget(editLabel);
    TQHBoxLayout *l21 = new TQHBoxLayout(l2, 0);
    m_edit = new KLineEdit(this);
    editLabel->setBuddy(m_edit);
    m_goSearchButton = new KPushButton(i18n("Se&arch"), this);
    l21->addWidget(m_edit);
    l21->addWidget(m_goSearchButton);

    TQGridLayout *l3 = new TQGridLayout(l, 2, 2, 0);
    m_searchMethodBox = new KComboBox(this);
    m_searchMethodBox->insertItem(i18n("and"));
    m_searchMethodBox->insertItem(i18n("or"));
    TQLabel *smLabel = new TQLabel(m_searchMethodBox, i18n("&Method:"), this);
    m_sortMethodBox = new KComboBox(this);
    m_sortMethodBox->insertItem(i18n("Score"));
    m_sortMethodBox->insertItem(i18n("Title"));
    m_sortMethodBox->insertItem(i18n("Date"));
    TQLabel *rmLabel = new TQLabel(m_sortMethodBox, i18n("S&ort by:"), this);
    l3->addWidget(smLabel, 0, 0);
    l3->addWidget(m_searchMethodBox, 0, 1);
    l3->addWidget(rmLabel, 1, 0);
    l3->addWidget(m_sortMethodBox, 1, 1);

    TQVBoxLayout *l4 = new TQVBoxLayout(l, 0);
    m_view = new TDEListView(this);
    TQLabel *vLabel = new TQLabel(m_view, i18n("Search &results:"), this);
    l4->addWidget(vLabel);
    l4->addWidget(m_view);

    TQHBoxLayout *l5 = new TQHBoxLayout(l, KDialog::spacingHint());
    m_configButton = new KPushButton(i18n("Update Config"), this);
    m_indexButton = new KPushButton(i18n("Update Index"), this);
    l5->addWidget(m_configButton);
    l5->addWidget(m_indexButton);
    l5->addItem(new TQSpacerItem(1, 1, TQSizePolicy::Expanding, TQSizePolicy::Fixed));

    l->addSpacing(2);

    m_view->setSorting(-1);
    m_view->addColumn(i18n("Relevance"));
    m_view->addColumn(i18n("Title"));
    m_view->setColumnWidthMode(0, TQListView::Maximum);
    m_view->setColumnWidthMode(1, TQListView::Maximum);
    m_view->setAllColumnsShowFocus(true);
    m_view->setResizeMode( TQListView::LastColumn );

    connect(m_configButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(updateConfig()));
    connect(m_indexButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(updateIndex()));
    connect(m_edit, TQT_SIGNAL(returnPressed()), this, TQT_SLOT(search()));
    connect(m_goSearchButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(search()));
    connect(m_view, TQT_SIGNAL(executed(TQListViewItem*)), this, TQT_SLOT(executed(TQListViewItem*)));
    connect(m_view, TQT_SIGNAL(mouseButtonPressed(int, TQListViewItem*, const TQPoint&, int )),
        this, TQT_SLOT(itemMouseButtonPressed(int, TQListViewItem*, const TQPoint&, int )));
}

SearchView::~SearchView()
{
}

void SearchView::updateConfig()
{
    runHtdig("-c");
}

void SearchView::updateIndex()
{
    runHtdig("-i");
    TDEConfig *config = m_part->config();
    config->setGroup("htdig");
    config->writeEntry("IsSetup", true);
    config->sync();
}

void SearchView::runHtdig(const TQString &arg)
{
    TDEProcess proc;
    proc << "tdevelop-htdig" << arg;
    proc.start(TDEProcess::DontCare);
}

void tqt_enter_modal(TQWidget *widget);
void tqt_leave_modal(TQWidget *widget);

void SearchView::search()
{
    TDEConfig *config = m_part->config();
    config->setGroup("htdig");
    if (config->readBoolEntry("IsSetup", false) == false)
    {
        KMessageBox::information(this, i18n("Full text search has to be set up before usage."));
        if (!m_part->configure(1))
            return;
        KMessageBox::information(this, i18n("Now the full text search database will be created.\nWait for database creation to finish and then repeat search."));
        updateIndex();
        return;
    }
    TQString exe = config->readPathEntry("htsearchbin", kapp->dirs()->findExe("htsearch"));
    if (exe.isEmpty())
    {
        KMessageBox::error(this, i18n("Cannot find the htsearch executable.\nIt is part of the ht://Dig package that is used by TDevelop to perform full text search. Please install ht://Dig and use Documentation page in Configure TDevelop dialog to set the htsearch location."));
        kdDebug() << "Can not find htsearch" << endl;
        return;
    }

    TQString indexdir = kapp->dirs()->saveLocation("data", "kdevdocumentation/search");
    TQDir d;
    if (indexdir.isEmpty() || !TQFile::exists(indexdir + "/htdig.conf"))
    {
        if (TQFile::exists("/var/lib/tdevelop3/helpindex/htdig.conf"))
            indexdir = "/var/lib/tdevelop3/helpindex";
        else if (TQFile::exists("/var/lib/tdevelop/helpindex/htdig.conf"))
            indexdir = "/var/lib/tdevelop/helpindex";

        if (!TQFile::exists(indexdir + "/htdig.conf"))
        {
            KMessageBox::error(this, i18n("Cannot find the htdig configuration file."));
            kdDebug() << "Cannot find the htdig configuration file" << endl;
            return;
        }
    }

    TQString savedir = kapp->dirs()->saveLocation("data", "kdevdocumentation/search");
    if (!d.exists(savedir))
        d.mkdir(savedir);

    TQString query = TQString("words=%1;method=%2;matchesperpage=%3;format=%4;sort=%5")
        .arg(m_edit->text())
        .arg(m_searchMethodBox->currentItem()==1? "or" : "and")
        .arg(50)
        .arg("builtin-short")
        .arg(m_sortMethodBox->currentItem()==2? "date" : m_sortMethodBox->currentItem()==1? "title" : "score");

    kdDebug(9002) << "starting kprocess" << endl;
    kdDebug(9002) << "htdig line:" << exe << " -c " << (indexdir + "/htdig.conf ") << query <<  endl;
    TDEProcess *proc = new TDEProcess;
    TQString picdir = kapp->dirs()->findResourceDir("data", "kdevdocumentation/pics/htdig.png");
    proc->setEnvironment("PICDIR", picdir);
    *proc << exe << "-c" << (indexdir + "/htdig.conf") << query;

    connect( proc, TQT_SIGNAL(receivedStdout(TDEProcess *,char*,int)),
             this, TQT_SLOT(htsearchStdout(TDEProcess *,char*,int)) );
    connect( proc, TQT_SIGNAL(processExited(TDEProcess *)),
             this, TQT_SLOT(htsearchExited(TDEProcess *)) );

    searchResult = "";

    if (!proc->start(TDEProcess::NotifyOnExit, TDEProcess::Stdout))
    {
        KMessageBox::error(this, i18n("Cannot start the htsearch executable."));
        kdDebug() << "process start failed" << endl;
        delete proc;
        return;
    }

    // While receiving data from the subprocess, we want
    // to block the user interface, but still get repaint
    // events. Hack taken from NetAccess...
    kapp->setOverrideCursor(waitCursor);
    TQWidget blocker(0, 0, WType_Dialog | WShowModal);
    tqt_enter_modal(&blocker);
    kapp->enter_loop();
    tqt_leave_modal(&blocker);
    kapp->restoreOverrideCursor();

    if (!proc->normalExit() || proc->exitStatus() != 0)
    {
        kdDebug() << "Error running htsearch... returning now" << endl;
        delete proc;
        return;
    }

    delete proc;

    // modify the search result
    searchResult = searchResult.replace(TQRegExp("http://localhost/"), "file:/");
    searchResult = searchResult.replace(TQRegExp("Content-type: text/html"), "");

    // dump the search result
    TQFile f(savedir + "/results.html");
    if (f.open(IO_WriteOnly))
    {
        TQTextStream ts(&f);
        ts << searchResult << endl;
        f.close();
    }

    //show results
    analyseSearchResults();
//    m_part->partController()->showDocument(KURL("file://" + indexdir + "/results.html"));
}

void SearchView::htsearchStdout(TDEProcess *, char *buffer, int len)
{
    searchResult += TQString::fromLocal8Bit(buffer, len);
}

void SearchView::htsearchExited(TDEProcess *)
{
    kapp->exit_loop();
}

void SearchView::analyseSearchResults()
{
    m_view->clear();
    TQTextStream str(searchResult, IO_ReadOnly);
    DocumentationItem *former = 0;
    while (!str.eof())
    {
        TQString line = str.readLine();

        TQRegExp starsExp("alt=\"\\*\"");
        starsExp.setMinimal(true);
        int stars = line.contains(starsExp);

        TQRegExp urlExp("<strong><a href=\"(.*)\">(.*)</a></strong>");
        if (urlExp.search(line)==-1)
            continue;
        TQString url = urlExp.cap(1);
        TQString title = urlExp.cap(2);

        TQString starsStr;
        for (int i = 0; i < stars; ++i)
            starsStr += "*";

        if (former)
            former = new DocumentationItem(DocumentationItem::Document, m_view, former, starsStr);
        else
            former = new DocumentationItem(DocumentationItem::Document, m_view, starsStr);
        former->setText(1, title);
        former->setURL(KURL(url));
    }

    executed( m_view->firstChild() );
}

void SearchView::executed(TQListViewItem *item)
{
    DocumentationItem *d = dynamic_cast<DocumentationItem*>(item);
    if (!d)
        return;

    m_part->partController()->showDocument(d->url());
}

void SearchView::itemMouseButtonPressed(int button, TQListViewItem *item, const TQPoint &pos, int // c
                                        )
{
    if ((button != Qt::RightButton) || (!item))
        return;
    DocumentationItem *docItem = dynamic_cast<DocumentationItem*>(item);
    if (!docItem)
        return;

    DocUtils::docItemPopup(m_part, docItem, pos, true, false, 1);
}

void SearchView::setSearchTerm(const TQString &term)
{
    m_edit->setText(term);
}

void SearchView::askSearchTerm()
{
    m_edit->setFocus();
}

void SearchView::focusInEvent(TQFocusEvent */*e*/)
{
    m_edit->setFocus();
}

#include "searchview.moc"