/***************************************************************************
 *   Copyright (C) 2005 by xavier helluy                                   *
 *   xavier.helluy@web.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.                                   *
 *                                                                         *
 *   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 Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/


//TODO:option to store text content inside database 
//TODO:option to store path and filename of files with no content and no metadata
//much more....
 
#include "indexerwidget.h"
#include <qstring.h>
#include <klineedit.h>
#include <kurlrequester.h>
#include <klistbox.h>
#include <kcombobox.h>
#include <kdebug.h>
#include <qmap.h>
#include <qcursor.h>

#include <kurlrequester.h>
#include <kurl.h>
#include <kconfig.h>
#include <kconfigbase.h>
#include <qcheckbox.h>
#include <qradiobutton.h>
#include <kfile.h>
#include <kstandarddirs.h>

#include "helpertestsstring.h"

#include <kguiitem.h>
#include <dcopclient.h>
#include <kclucene_indexer_daemon_dcopiface_stub.h>
#include <kapplication.h>

#include <unistd.h>      //02.06.05 Jol  sleep or QThread::sleep (protected) 


IndexerWidget::IndexerWidget(
				KDialogBase * dialog,
				Settings * settings , 
				QMap<QString , QString > * indexMap ,
				QMap<QString , QString > * textConvertersMap ,
				QWidget* parent, 
				const char* name, 
				WFlags fl
			    )
				: DCOPObject("CLuceneIndexerWidget"),
				IndexerWidgetBase(parent,name,fl),
 			      	IndexerWidgetDCOPIface()
  			      
{
	m_dialog = dialog;
	m_settings = settings;
	m_indexMap = indexMap;
	m_textConvertersNameMap = textConvertersMap;
	//look of buttons
	startIndexing->setGuiItem(KGuiItem(
			"Start indexing",
	"launch",
	"start indexing",
	QString::null));
		
	//only new names can be entered
	this->indexName->setDuplicatesEnabled(FALSE);
	this->selectExclude->setShowLocalProtocol(true);
	this->selectInclude->setShowLocalProtocol(true);
	
	//files or dirs can be entered
	this->selectInclude->setMode(KFile::File|KFile::Directory);
	
	
	connect ( this->indexName, SIGNAL(activated(const QString &)),
		  this, SLOT(someIndexSelected(const QString &)));

	connect ( this->startIndexing, SIGNAL(clicked()),
		  this, SLOT(slotStartIndexing()));
	
	connect ( this->killDCOP, SIGNAL(clicked()),
		  this, SLOT(killDCOPIndexer()));
	

	updateEntriesInWidget( m_indexMap->begin().key() );

	
}

IndexerWidget::~IndexerWidget()
{
}

void IndexerWidget::slotNewTextConverter(const QString& itemStr)
{
	kdDebug() << "IndexerWidget::slotNewTextConverter() CALLED" << itemStr << endl;
// 	this->textConvertersName->insertItem (itemStr);	
	QString name = itemStr.mid(14,itemStr.length());	

	this->textConvertersName->insertItem(name);


}
void IndexerWidget::slotRemoveTextConverter(const QString& itemStr)
{
	kdDebug() << "IndexerWidget::slotRemoveTextConverter() CALLED" << itemStr << endl;
	QString name = itemStr.mid(14,itemStr.length());	

	this->textConvertersName->setCurrentText(name);
	this->textConvertersName->removeItem
			(this->textConvertersName->currentItem());	
}


void IndexerWidget::indexerFinished()
{
	this->startIndexing->setEnabled(TRUE);
	this->setCursor(QCursor(ArrowCursor));

// 	toggle();
	
}
/////////////////WIDGET INTERNALS////////////////////////////////
void IndexerWidget::addItemInclude()
{
	QString itemStr = this->selectInclude->url();
	if ((!itemStr.isEmpty()) && 
		    ( HelperTestsString::isValidURLString(itemStr) ||HelperTestsString::isValidPathString(itemStr) )
	   )
		if ( !this->includeItems->findItem(itemStr , Qt::ExactMatch) )
	{
			this->includeItems->insertItem(itemStr);
			this->includeItems->sort();
			m_itemIncludeList.append(itemStr);
	}
}

void IndexerWidget::addItemExclude()
{
	//FIXME:check if url+"/" or not entered
	QString itemStr = this->selectExclude->url();
	if ((!itemStr.isEmpty()) && 
		     ( HelperTestsString::isValidURLString(itemStr) 
		     ||HelperTestsString::isValidPathString(itemStr)))
	{
		if ( !this->excludeItems->findItem(itemStr , Qt::ExactMatch) )
		{
			this->excludeItems->insertItem(itemStr);
			this->excludeItems->sort();
			m_itemExcludeList.append(itemStr);
		}	
	}
}

void IndexerWidget::removeItemInclude()
{
	m_itemIncludeList.remove(this->includeItems->currentText());
	this->includeItems->removeItem( this->includeItems->currentItem() );
	

}

void IndexerWidget::removeItemExclude()
{
	m_itemExcludeList.remove(this->excludeItems->currentText());
	this->excludeItems->removeItem( this->excludeItems->currentItem() );

}

void IndexerWidget::slotNewIndexConfigViewUpdate(const QString & indexStr)
{
	//change the name of the index to a non existing index name.
	this->indexLocation->setURL(indexStr + ".cl_index");
	
}



////////////////////////////// IMPL HERE ///////////////////////////////

void IndexerWidget::saveSettingsIndexCreator()
{
	kdDebug() << "IndexerWidget::saveSettingsIndexCreator() CALLED" << endl;

	//check if already exists
	QString index = this->indexName->currentText(); 
	QString groupName = "clucene_Index-" +index;
	QString res = m_indexMap->find(groupName).key();
	if (!(res==QString::null))
	{
		kdDebug() << "MainGUI::EXISTS ALREADY" << endl;
		storeEntries(groupName);
	}
	else
	{
		// if the user pressed retrurn , the indexName cleanness has been checked
		// but if the user only entered something or nothing, then this is not checked
		// do it here.
		kdDebug() << "MainGUI::NEW GROUP" << endl;
		if (HelperTestsString::isValidFileName(index))
		{
			m_indexMap->insert ( groupName , m_settings->indexLocation() );
			storeEntries(groupName);
		}
		
	}
}

//called when a new index names is selected or highlighted from within IndexCreator
void IndexerWidget::someIndexSelected( const QString & itemStr)
{
	kdDebug() << "MainGUI::someIndexSelected() CALLED : " <<  itemStr << endl;

	//reset m_indexMap
	//check if it is a new entry
	QMap<QString , QString>::Iterator it = m_indexMap->find("clucene_Index-"+itemStr);
	
	if ( it == m_indexMap->end())
	{
		//new entry found
		//check it is "clean": it must be a filenmae only no protocol accepted
		if (!HelperTestsString::isValidFileName(itemStr))
		{
			//name is not clean
			this->indexName->removeItem (this->indexName->currentItem());	
		}
		else
		{
			//name is new and is clean
			
			//change the index Location to a default one
			this->indexLocation->setURL(itemStr + ".cl_index");
			this->indexLocation->setEnabled(TRUE);
			//clear the list of include/exclude dirs
			this->includeItems->clear();
			this->excludeItems->clear();
			
			//remove the item from the combobox because apply is not here yet
			this->indexName->removeItem (this->indexName->currentItem());
			
			this->indexName->setEditText(itemStr);

			m_itemIncludeList.clear();
			m_itemExcludeList.clear();
			//that's it
		}
	}
	else
	{
		//this is an already existing entry
		//read the group and update the gui.
		
		//create a group name
		QString groupName = "clucene_Index-" + itemStr;
		
		//check group is valid
		bool validEntries = validateStoredIndexEntries(groupName);
		
		//update the widget
		if (!validEntries)
		{
			//FIXME:create defaults entries
		}
		else
		{
			//stored entries are valid:
			
			//reset internal lists
			m_itemIncludeList.clear();
			m_itemExcludeList.clear();

			//update the widget:
			updateEntriesInWidget( groupName );
			
			//forbid modifications of the indexLocation
			this->indexLocation->setEnabled(FALSE);
		}			
	}
}	

bool IndexerWidget::validateStoredIndexEntries( const QString & /*indexGroup*/)
{
	return TRUE;
}

void IndexerWidget::updateEntriesInWidget(const QString & indexGroup)
{
	kdDebug() << "IndexerWidget::updateEntriesInWidget()" << indexGroup <<endl;

	//update converter list
	QMap<QString , QString>::Iterator it2;
	QStringList textConvertersNames;
	this->textConvertersName-> clear();		
	for ( it2 = m_textConvertersNameMap->begin(); it2 != m_textConvertersNameMap->end(); ++it2 )
	{
		kdDebug() << "IndexerWidget::updateEntriesInWidget()" << *it2 <<endl;

		QString name = it2.key().mid(14,it2.key().length());	
		kdDebug() << "IndexerWidget::updateEntriesInWidget()" << name <<endl;

		textConvertersNames.append(name);
	}
	kdDebug() << "IndexerWidget::updateEntriesInWidget() list: " << textConvertersNames <<endl;

	this->textConvertersName-> insertStringList(textConvertersNames);
	//---------end of update converter list----------------------------
	
	//update indexes list
	this->indexName             -> clear();
		
	QStringList indexNames;
	QMap<QString , QString>::Iterator it;
		
		//FIXME:use real name
	for ( it = m_indexMap->begin(); it != m_indexMap->end(); ++it )
	{
		QString name = 		it.key().mid(14,it.key().length());	 
		kdDebug() << "MainGUI::key" <<  name<<endl;
		indexNames.append(name);
	}
// 		this->indexName->clear();
	this->indexName             -> insertStringList(indexNames);
	//---------end of update indexes list----------------------------
		
	//update selected indexes 
	//FIXME: using groupList -> sync necessery 
	KConfig * cfg = m_settings->config();
	QStringList tmp_gl = cfg->groupList();
	
	if (this->indexName->currentText()  == "clucene_Index-Current")
	{
		//update to defaults , not in use so far
	}
	else if (!(tmp_gl.find(indexGroup)==tmp_gl.end()))
	{		
		kdDebug() << "IndexerWidget::updateEntriesInWidget()" << indexGroup <<endl;

		KConfig * cfg = m_settings->config();
		cfg->setGroup(indexGroup);
		
		this->indexName             -> setCurrentText( cfg->readEntry( "indexName"         ) );

		
		//setup to indexgroup text converter group if it is still existing
		QString tmpConvertersName = cfg->readEntry( "textConverterName");
		QString tmpLongConvertersName = "cl_Converters-" + tmpConvertersName;
		
		QString tmpMediaType = cfg->readEntry    ( "mediaType"  );
		if (tmpMediaType.isEmpty())
			tmpMediaType = "unknown";
		if (!(tmp_gl.find(tmpLongConvertersName)==tmp_gl.end()))
		{
			this->textConvertersName-> setCurrentText( tmpConvertersName );
		}
		else
		{
			kdDebug() << "IndexerWidget::updateEntriesInWidget() CONV GROUP NOT FOUND" <<endl;
		}
				
		this->indexLocation         -> setURL(         cfg->readEntry( "indexLocation"     ) );
		
//		this-> autoIndexLocation-> setChecked( cfg->readBoolEntry( "autoIndexLocation" ) );
		this-> boolMetaData     -> setChecked( cfg->readBoolEntry( "boolMetaData"      ) );
		this-> boolContent      -> setChecked( cfg->readBoolEntry( "boolContent"       ) );
		this-> boolArchives      -> setChecked( cfg->readBoolEntry( "boolArchives"      ) );
		this->restrictMetaData->setChecked(cfg->readBoolEntry( "restrictMetaData"));
		
		this-> includeSubFolders-> setChecked( cfg->readBoolEntry( "includeSubFolders" ) );
		this-> excludeSubFolders-> setChecked( cfg->readBoolEntry( "excludeSubFolders" ) );
		
		this->includeItems          ->clear();
		this->includeItems          -> insertStringList (cfg->readListEntry("includeItems" ) );
		m_itemIncludeList = cfg->readListEntry("includeItems" );
		
		this->excludeItems          ->clear();
		this->excludeItems          -> insertStringList (cfg->readListEntry( "excludeItems") );
		m_itemExcludeList = cfg->readListEntry("excludeItems" );
		
		this-> addToIndex       -> setChecked( cfg->readBoolEntry( "addToIndex"        ) );
		this-> eraseIndex       -> setChecked( cfg->readBoolEntry( "eraseIndex"        ) );
		this-> eraseMedia       -> setChecked( cfg->readBoolEntry( "eraseMedia"        ) );
		this-> updateIndex      -> setChecked( cfg->readBoolEntry( "updateIndex"       ) );
		this-> syncIndex        -> setChecked( cfg->readBoolEntry( "syncIndex"         ) );
		
		this->mediaName		-> setText   ( cfg->readEntry    ( "mediaName"         ) );
		this->mediaType		-> setCurrentText   ( tmpMediaType );
		
		this->indexLocation->setEnabled(FALSE);
	}
}

void IndexerWidget::storeEntries( QString & indexGroup )
{
	//activate from apply slot
	//read all entries and write to disk
	kdDebug() << "MainGUI::storeEntries()" <<  indexGroup << endl;

	KConfig * cfg = m_settings->config();
	cfg->setGroup(indexGroup);
	
	
	cfg->writeEntry( "indexName"    , this->indexName->currentText());//FIXME:should be strip(indexGroup)
	
	//check if valid pathname, if not write an empty string list. Only accept localpath and relative names
	QString urlString = this->indexLocation->url();
	if (!HelperTestsString::isValidPathString(urlString))
	{
		if (!HelperTestsString::isValidFileName(urlString))
		{
			cfg->writeEntry( "indexLocation" , "");
		}
		else
		{
			cfg->writeEntry( "indexLocation" , this->indexLocation->url());
		}
	}
	else			
	{	
		cfg->writeEntry( "indexLocation" , this->indexLocation->url());
	}
	
	cfg->writeEntry( "boolMetaData"      , this->boolMetaData     ->isChecked());
	cfg->writeEntry( "boolContent"       , this->boolContent      ->isChecked());
	cfg->writeEntry( "boolArchives"       , this->boolArchives     ->isChecked());
	cfg->writeEntry( "excludeSubFolders" , this->excludeSubFolders->isChecked());
	cfg->writeEntry( "includeSubFolders" , this->includeSubFolders->isChecked());
	cfg->writeEntry( "addToIndex"        , this->addToIndex       ->isChecked());
	cfg->writeEntry( "eraseIndex"        , this->eraseIndex       ->isChecked());
	cfg->writeEntry( "eraseMedia"        , this->eraseMedia       ->isChecked());
	cfg->writeEntry( "updateIndex"       , this->updateIndex      ->isChecked());
	cfg->writeEntry( "syncIndex"         , this->syncIndex        ->isChecked());
	cfg->writeEntry( "includeItems" , this->m_itemIncludeList  );
	cfg->writeEntry( "excludeItems" , this->m_itemExcludeList  );
	
	cfg->writeEntry( "restrictMetaData", 	this->restrictMetaData->isChecked());

	cfg->writeEntry( "textConverterName", this->textConvertersName->currentText());

	cfg->writeEntry( "mediaName",  this->mediaName->text());
	cfg->writeEntry( "mediaType", this->mediaType->currentText()  );
	
	cfg->sync();
	
	//FIXME:hack button apply
	m_dialog->enableButtonApply(true);
	
	if (!(cfg->groupList().find(indexGroup)==cfg->groupList().end()))
	{
		kdDebug() << "NEWINDEX CREATED SIGNAL " << endl;

		kdDebug() << m_indexMap->keys()<<endl;

		emit newIndex(indexGroup);
		
	}
}

void IndexerWidget::slotTextIndexNameChanged(const QString & str)
{
	this->indexLocation->setURL(str + ".cl_index");
	this->indexLocation->setEnabled(TRUE);

}

void IndexerWidget::slotRemoveIndex(const QString & /*removedIndex*/)
{
	updateEntriesInWidget( m_indexMap->begin().key());

}


void IndexerWidget::slotStartIndexing()
{
	//store settings
	saveSettingsIndexCreator();
	
	//reparse whole configuration
	KConfig * cfg = m_settings->config();
	cfg->reparseConfiguration();
	
	//wake dcop
	wakeDCOP();
	
	//send query
	QString indexEntry = "clucene_Index-" + this->indexName->currentText() ;
	QString test = m_indexMap->find(indexEntry).key();
	if (!(test==QString::null))
	{
	//Good, there is a stored entry.
	cfg = m_settings->config();
	cfg->setGroup(indexEntry);
	QString str(HelperTestsString::name2path( cfg->readEntry("indexLocation"))) ;
	kdDebug() << "START: indexLocation:" <<	str << endl;
	
// 	QString output;
// 	pMyCLuceneDemo->startIndexing(IndexLocation , FilesToIndexLocation , output);
// 	this->viewOutput->setText(output);
	
	

	//cout << "Enter index location: ";
        //get CONVERSION HERE FOR NOW, BUT SHOULD BE DONE BEFORE COMING HERE
		
	QStringList includeList = cfg->readListEntry("includeItems");
	kdDebug() << "START: LIST:" <<	includeList << endl;

	QString indexPath =  HelperTestsString::name2path( cfg->readEntry("indexLocation"));
	


	//get converters
	QString currentGroup = cfg->group();
	
	QStringList tmpMimeList;
	QStringList tmpMimeProc; 
	QStringList tmpMimeArgs;
	QStringList tmpMimeOutput;
	QStringList tmpPatternList;
	QStringList tmpPatternProc;
	QStringList tmpPatternArgs;
	QStringList tmpPatternOutput;
	bool tmpText,tmpXml , tmpHtml, tmpRestrictMetaData; 

	QString convGroup =  "cl_Converters-" + cfg->readEntry("textConverterName");
	QStringList tmp_gl = cfg->groupList();
	if (tmp_gl.find(convGroup)==tmp_gl.end())
	{
		//FIXME do something here !!!
		kdDebug() << "ERRRRRRRRRROR CONVERTER GROUP DOESNOT EXISTS"  << endl;
	}
	else
	{
		cfg->setGroup(convGroup);
		tmpMimeList	=	cfg->readListEntry( "MimeList");
		tmpPatternList	=	cfg->readListEntry( "PatternList");

		tmpMimeList	=	cfg->readListEntry( "MimeList");
		tmpPatternList	=	cfg->readListEntry( "PatternList");
		tmpMimeProc	=	cfg->readListEntry( "MimeProc");
		tmpMimeArgs	=	cfg->readListEntry( "MimeArgs");
		tmpMimeOutput	=	cfg->readListEntry( "MimeOutput");
		tmpPatternProc	=	cfg->readListEntry( "PatternProc");
		tmpPatternArgs	=	cfg->readListEntry( "PatternArgs");
		tmpPatternOutput=	cfg->readListEntry( "PatternOutput");
		tmpText = cfg->readBoolEntry		( "text"       );
		tmpHtml = cfg->readBoolEntry		( "html"       );
		tmpXml = cfg->readBoolEntry		( "xml"       );
		tmpRestrictMetaData = cfg->readBoolEntry( "restrictMetaData"       );
// 		//TODO:check that lengths of lists is ok		
		
	}
	//go back to curtrenz indexfroup
	cfg->setGroup(currentGroup);

	//dcop ui
	this->startIndexing->setEnabled(FALSE);
	this->setCursor(QCursor(BusyCursor));

	
	KCLuceneIndexerDaemonDCOPIface_stub iface("kclucene-indexer-daemon" , "KCLuceneIndexerDaemon");
	iface.doIndex( 
		kapp->dcopClient()->appId(),
		HelperTestsString::name2path(cfg->readEntry    	( "indexName"         )),
		HelperTestsString::name2path(cfg->readEntry    	( "indexLocation"     )),
 		cfg->readBoolEntry		( "boolMetaData"      ),
 		cfg->readBoolEntry		( "boolContent"       ),
 		cfg->readBoolEntry		( "boolArchives"      ),
 		cfg->readBoolEntry		( "includeSubFolders" ),
 		cfg->readBoolEntry		( "excludeSubFolders" ),
 		cfg->readListEntry		("includeItems"       ),
 		cfg->readListEntry		( "excludeItems"      ),
 		cfg->readBoolEntry		( "addToIndex"        ),
 		cfg->readBoolEntry		( "eraseIndex"        ),
		cfg->readBoolEntry		( "eraseMedia"        ),
		cfg->readBoolEntry		( "updateIndex"       ),
		cfg->readBoolEntry		( "syncIndex"         ),
		tmpMimeList,
		tmpMimeProc,
		tmpMimeArgs,
		tmpMimeOutput,
		tmpPatternList,		
		tmpPatternProc,
		tmpPatternArgs,
		tmpPatternOutput,
		tmpText,
		tmpHtml,
		tmpXml,
		tmpRestrictMetaData,
		cfg->readEntry			( "mediaType"),
		cfg->readEntry			( "mediaName")
		     );

}

	
}

//HACK: the dcop indexer needs to implement threading, then one can send to the dcop-indexer instance the threadID to kill or stop. 
//TODO: Better would be to implement a asynchroneous indexing a la kdirstat
//then one could stop indexing in a clean way whenever requested by the user.
bool IndexerWidget::killDCOPIndexer()
{
	kdDebug() << "IndexerWidget::killDCOPIndexer() :pid"   << endl;

	QStringList args;
	//args.append(QString::number(*m_dcopIndexerPid));
	args.append("-9");
	args.append("kclucene-indexer-daemon");
	KApplication::kdeinitExecWait("killall", 
			      args, 
			      0, 
			      0, 
			      0
				     );
	indexerFinished();

	return TRUE;	
}
		

bool IndexerWidget::wakeDCOP()
{
	int counter = -1;
	// call and wake up the indexer daemon service 
	fprintf(stderr, "Start wake up \n");
	//if (!m_dcop->isApplicationRegistered("kclucene-search-daemon")) 
	if (!DCOPClient::mainClient()->isApplicationRegistered("kclucene-indexer-daemon")) 
	{
		QString error;
		fprintf(stderr, "I have to wake you up\n");
		if (!(KApplication::startServiceByDesktopName("kclucene_indexer_daemon", QStringList(),
		    &error , 0  , 0 , 0, TRUE) == 0))
		{
			fprintf(stderr, "Starting cLuceneDcopIndexerDaemon failed with message " +
					error +"\n" );
			return FALSE;  //bad
		}
		else
		{
			fprintf(stderr, "Good I did it \n");
			while (counter < 5)
			{
				counter++;
				sleep(counter);	
				if (DCOPClient::mainClient()->isApplicationRegistered("kclucene-indexer-daemon")) 
					return TRUE;
			}
			fprintf(stderr, "Hum I did start something, but cannot find its dcop intrerface \n");
			return FALSE;
				
		}
			
	}
	else
		fprintf(stderr, "You are already running !!!\n");
	return TRUE;  //good
}



#include "indexerwidget.moc"

