/***************************************************************************
 *   Copyright (C) 2004 by Kazuki Ohta                                     *
 *   mover@hct.zaq.ne.jp                                                   *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "kkcontactquicksearchline.h"
#include "kkaddressbookdialog.h"
#include "kkcontactdata.h"
#include "kkaddressbookglobalconfig.h"

#include <qapplication.h>
#include <qlabel.h>
#include <qtimer.h>
#include <qpopupmenu.h>

#include <kglobal.h>
#include <kconfig.h>
#include <kaction.h>
#include <kiconloader.h>
#include <klistview.h>
#include <klocale.h>
#include <kcombobox.h>

KKContactQuickSearchLine::KKContactQuickSearchLine( QWidget *parent,
					    KListView *listView,
					    const char *name )
	: KKContactQuickSearchLineBase(parent, listView, name), mGroupCombo(0), mSelectedGroupIndex(0)
{
	QLabel *label = new QLabel( i18n("Group:"), parent );

	mGroupCombo = new KComboBox( parent );
	mGroupCombo->insertItem( i18n("Any Group") );
        // insert groups
        QStringList groupList = KKAddressBookGlobalConfig::readGroupList();
        QStringList::ConstIterator it = groupList.begin();
	const QStringList::ConstIterator end = groupList.end();
	for( ; it != end; ++it )
	{
		mGroupCombo->insertItem( *it );		
	}
                                
	connect( mGroupCombo, SIGNAL ( activated( int ) ),
		 this, SLOT( slotGroupChanged( int ) ) );

	label->setBuddy( mGroupCombo );
}

KKContactQuickSearchLine::~KKContactQuickSearchLine()
{
}

void KKContactQuickSearchLine::updateGroups()
{
	QString currentText = mGroupCombo->currentText();
	QStringList newGroupList = KKAddressBookGlobalConfig::readGroupList();

	// delete
	mGroupCombo->clear();

        // any group should always available
        mGroupCombo->insertItem( i18n("Any Group") );

	// add new
	QStringList::ConstIterator it = newGroupList.begin();
	const QStringList::ConstIterator end = newGroupList.end();
	for( ; it != end; ++it )
	{
		mGroupCombo->insertItem( *it );		
	}

	// select
	if( mGroupCombo->contains( currentText ) )
	{
		mGroupCombo->setCurrentItem( currentText );
	}

	// update
	updateSearch();
}

bool KKContactQuickSearchLine::itemMatches(const QListViewItem *i, const QString &s) const
{
	if( i && mSelectedGroupIndex != 0 )
	{
		// Matching By Group
		KKContactListViewItem *item = (KKContactListViewItem*)i;
		const KKContactData *data = item->contactData();
		QString targetGroup = mGroupCombo->text( mSelectedGroupIndex );

		if( QString::compare( data->getGroup(), targetGroup ) != 0 )
			return false;
	}
	
	return KKContactQuickSearchLineBase::itemMatches( i, s );
}

void KKContactQuickSearchLine::reset()
{
	clear();
	mGroupCombo->setCurrentItem( 0 );
	slotGroupChanged( 0 );
}

void KKContactQuickSearchLine::slotGroupChanged( int index )
{
    mSelectedGroupIndex = index;

    updateSearch();
}

//-----------------------------------------------------------------------------------
/**
 * The source code below comes from kdelibs/kdeui/klistviewsearchline.cpp
 * because KListViewSearchLine isn't included before KDE3.3 (aka, KDE3.2, KDE3.1, ...)
 */
#define KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID 2004

class KKContactQuickSearchLineBase::KKContactQuickSearchLineBasePrivate
{
public:
    KKContactQuickSearchLineBasePrivate() :
        listView(0),
        caseSensitive(false),
        activeSearch(false),
        keepParentsVisible(true),
        queuedSearches(0) {}

    KListView *listView;
    bool caseSensitive;
    bool activeSearch;
    bool keepParentsVisible;
    QString search;
    int queuedSearches;
    QValueList<int> searchColumns;
};

////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////

KKContactQuickSearchLineBase::KKContactQuickSearchLineBase(QWidget *parent, KListView *listView, const char *name) :
    KLineEdit(parent, name)
{
    d = new KKContactQuickSearchLineBasePrivate;

    d->listView = listView;

    connect(this, SIGNAL(textChanged(const QString &)),
            this, SLOT(queueSearch(const QString &)));

    if(listView) {
        connect(listView, SIGNAL(destroyed()),
                this, SLOT(listViewDeleted()));

        connect(listView, SIGNAL(itemAdded(QListViewItem *)),
                this, SLOT(itemAdded(QListViewItem *)));
    }
    else
        setEnabled(false);
}

KKContactQuickSearchLineBase::KKContactQuickSearchLineBase(QWidget *parent, const char *name) :
    KLineEdit(parent, name)
{
    d = new KKContactQuickSearchLineBasePrivate;

    d->listView = 0;

    connect(this, SIGNAL(textChanged(const QString &)),
            this, SLOT(queueSearch(const QString &)));

    setEnabled(false);
}

KKContactQuickSearchLineBase::~KKContactQuickSearchLineBase()
{
    delete d;
}

bool KKContactQuickSearchLineBase::caseSensitive() const
{
    return d->caseSensitive;
}

QValueList<int> KKContactQuickSearchLineBase::searchColumns() const
{
    return d->searchColumns;
}

bool KKContactQuickSearchLineBase::keepParentsVisible() const
{
    return d->keepParentsVisible;
}

KListView *KKContactQuickSearchLineBase::listView() const
{
    return d->listView;
}

////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

void KKContactQuickSearchLineBase::updateSearch(const QString &s)
{
    if(!d->listView)
        return;

    d->search = s.isNull() ? text() : s;

    // If there's a selected item that is visible, make sure that it's visible
    // when the search changes too (assuming that it still matches).

    QListViewItem *currentItem = 0;

    switch(d->listView->selectionMode())
    {
    case KListView::NoSelection:
        break;
    case KListView::Single:
        currentItem = d->listView->selectedItem();
        break;
    default:
    {
        int flags = QListViewItemIterator::Selected | QListViewItemIterator::Visible;
        for(QListViewItemIterator it(d->listView, flags);
            it.current() && !currentItem;
            ++it)
        {
            if(d->listView->itemRect(it.current()).isValid())
                currentItem = it.current();
        }
    }
    }

    if(d->keepParentsVisible)
        checkItemParentsVisible(d->listView->firstChild());
    else
        checkItemParentsNotVisible();

    if(currentItem)
        d->listView->ensureItemVisible(currentItem);
}

void KKContactQuickSearchLineBase::setCaseSensitive(bool cs)
{
    d->caseSensitive = cs;
}

void KKContactQuickSearchLineBase::setKeepParentsVisible(bool v)
{
    d->keepParentsVisible = v;
}

void KKContactQuickSearchLineBase::setSearchColumns(const QValueList<int> &columns)
{
    d->searchColumns = columns;
}

void KKContactQuickSearchLineBase::setListView(KListView *lv)
{
    if(d->listView) {
        disconnect(d->listView, SIGNAL(destroyed()),
                   this, SLOT(listViewDeleted()));

        disconnect(d->listView, SIGNAL(itemAdded(QListViewItem *)),
                   this, SLOT(itemAdded(QListViewItem *)));
    }

    d->listView = lv;

    if(lv) {
        connect(d->listView, SIGNAL(destroyed()),
                this, SLOT(listViewDeleted()));

        connect(d->listView, SIGNAL(itemAdded(QListViewItem *)),
                this, SLOT(itemAdded(QListViewItem *)));
    }

    setEnabled(bool(lv));
}

////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////

bool KKContactQuickSearchLineBase::itemMatches(const QListViewItem *item, const QString &s) const
{
    if(s.isEmpty())
        return true;

    // If the search column list is populated, search just the columns
    // specifified.  If it is empty default to searching all of the columns.

    if(!d->searchColumns.isEmpty()) {
        QValueList<int>::ConstIterator it = d->searchColumns.begin();
        for(; it != d->searchColumns.end(); ++it) {
            if(*it < item->listView()->columns() &&
               item->text(*it).find(s, 0, d->caseSensitive) >= 0)
                return true;
        }
    }
    else {
        for(int i = 0; i < item->listView()->columns(); i++) {
            if(item->listView()->columnWidth(i) > 0 &&
               item->text(i).find(s, 0, d->caseSensitive) >= 0)
            {
                return true;
            }
        }
    }

    return false;
}

QPopupMenu *KKContactQuickSearchLineBase::createPopupMenu()
{
    QPopupMenu *popup = KLineEdit::createPopupMenu();

    QPopupMenu *subMenu = new QPopupMenu(popup);
    connect(subMenu, SIGNAL(activated(int)), this, SLOT(searchColumnsMenuActivated(int)));

    popup->insertSeparator();
    popup->insertItem(i18n("Search Columns"), subMenu);
    
    subMenu->insertItem(i18n("All Visible Columns"), KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID);
    subMenu->insertSeparator();
    
    bool allColumnsAreSearchColumns = true;
    for(int i = 0; i < d->listView->columns(); i++) {
        subMenu->insertItem(d->listView->columnText(i), i);
        if(d->searchColumns.isEmpty() || d->searchColumns.find(i) != d->searchColumns.end())
            subMenu->setItemChecked(i, true);
        else
            allColumnsAreSearchColumns = false;
    }
    subMenu->setItemChecked(KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID, allColumnsAreSearchColumns);
    
    // searchColumnsMenuActivated() relies on one possible "all" representation
    if(allColumnsAreSearchColumns && !d->searchColumns.isEmpty())
      d->searchColumns.clear();
    
    return popup;   
}    

////////////////////////////////////////////////////////////////////////////////
// protected slots
////////////////////////////////////////////////////////////////////////////////

void KKContactQuickSearchLineBase::queueSearch(const QString &search)
{
    d->queuedSearches++;
    d->search = search;
    QTimer::singleShot(200, this, SLOT(activateSearch()));
}

void KKContactQuickSearchLineBase::activateSearch()
{
    d->queuedSearches--;

    if(d->queuedSearches == 0)
        updateSearch(d->search);
}

////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

void KKContactQuickSearchLineBase::itemAdded(QListViewItem *item) const
{
    item->setVisible(itemMatches(item, text()));
}

void KKContactQuickSearchLineBase::listViewDeleted()
{
    d->listView = 0;
    setEnabled(false);
}

void KKContactQuickSearchLineBase::searchColumnsMenuActivated(int id)
{
    if(id == KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID) {
        if(d->searchColumns.isEmpty())
            d->searchColumns.append(0);
        else
            d->searchColumns.clear();
    }
    else {
        if(d->searchColumns.find(id) != d->searchColumns.end())
            d->searchColumns.remove(id);
        else {
            if(d->searchColumns.isEmpty()) {
                for(int i = 0; i < d->listView->columns(); i++) {
                    if(i != id)
                        d->searchColumns.append(i);
                }
            }
            else
                d->searchColumns.append(id);
        }
    }
    updateSearch();
}

////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////

void KKContactQuickSearchLineBase::checkItemParentsNotVisible()
{
    QListViewItemIterator it(d->listView);
    for(; it.current(); ++it)
    {
        QListViewItem *item = it.current();
        if(itemMatches(item, d->search))
            item->setVisible(true);
        else
            item->setVisible(false);
    }
}

bool KKContactQuickSearchLineBase::checkItemParentsVisible(QListViewItem *item)
{
    bool visible = false;
    for(; item; item = item->nextSibling()) {
        if((item->firstChild() && checkItemParentsVisible(item->firstChild())) ||
           itemMatches(item, d->search))
        {
            item->setVisible( true );
            visible = true;
        }
        else
            item->setVisible(false);
    }
    return visible;
}
