/***************************************************************************
                          KDBSearchEngine.cpp  -  description
                             -------------------
    begin                : Fri Sep 8 2000
    copyright            : (C) 2000 by Andrea Rizzi
    email                : rizzi@kde.org
 ***************************************************************************/

/*
 Translation search engine


  Copyright  2000
  Andrea Rizzi rizzi@kde.org

  License GPL v 2.0

*/

#include <qtextedit.h>
#include <qprogressdialog.h>

#include <qinputdialog.h>
#include <klocale.h>
#include <kdebug.h>
#include <kio/netaccess.h>
#include <kmessagebox.h>
#include <kfiledialog.h>
#include <kurlrequester.h>
#include <kstandarddirs.h>

#include "kapplication.h"
#include "KDBSearchEngine.h"

#include "dbscan.h"

#include "errno.h"
#include "stdio.h"
#include "stdlib.h"
#include <sys/time.h>
#include "preferenceswidget.h"
#include "dbse_factory.h"
#include <qprogressbar.h>
#include <qpushbutton.h>
#include <klineedit.h>
#include <kconfig.h>

#include <qdir.h>
#include <qradiobutton.h>
#include <qcheckbox.h>
#include <qspinbox.h>
#include <qslider.h>
#include <qmemarray.h>

#include "database.h"

#include "catalogsettings.h"

#define BIGNUMBER 400000000

KDBSearchEngine::KDBSearchEngine(QObject *parent,const char*name)
 : SearchEngine(parent,name)
{
edited="unknown";
dm=0;   //Database Manager
pw=0;   //Preference widget
lang="";
dbOpened=false;
dbname="";
lasterror=i18n("No error");
connect(this,SIGNAL(hasError(QString)),SLOT(setLastError(QString)));

  IAmReady=true;  //  I don't know if it is a good idea, no DB loaded!!!

  scanInProgress=false;
  searching=false;
  stopNow=false;

  norm=false;   // Normalize white space = FALSE
  comm=true;    // Remove Comments = TRUE
  
}


KDBSearchEngine::~KDBSearchEngine()
{
}



bool KDBSearchEngine::loadDatabase(QString database,bool noask=false)
{
   bool ret=true;
   bool asked=false;
   if(noask)  asked=true;   
   if(dm!=0)
   {
       delete dm;
       dm=0;
   }

   QDir dir(database);
   if(!dir.exists())
   {
       if(asked || KMessageBox::questionYesNo(0,i18n(
            "Database directory does not exist:\n%1\n"
            "Do you want to create it now?").arg(database))
               ==KMessageBox::Yes)
       {
           asked=true;
           
           QStringList dirList;
           while(!dir.exists() && !dir.dirName().isEmpty())
           {
              dirList.prepend(dir.dirName());
              dir.setPath(dir.path()+"/..");
           }

           for(QStringList::Iterator it = dirList.begin(); it!=dirList.end()
                   ; ++it)
           {
              if(!dir.mkdir(*it))
              {
                  KMessageBox::sorry(0,
                       i18n("It was not possible to create directory %1")
                       .arg(dir.path()+"/"+(*it)) );
                  ret=false;
                  break;
              }
              dir.cd(*it);
           }
       }
       else
       {
           ret=false;
       }
   }
 
  if(ret)
  {
      // test, if there are both of ,old and standard databases
      QString transFile = database+"/translations."+lang+".db";
      
      bool oldstuff = QFile::exists( transFile+",old" );
      bool newstuff = QFile::exists( transFile );
      
      if( oldstuff && newstuff ) {
        // there is an old db2 database, ask user
	
	if( KMessageBox::questionYesNo(0
	    ,i18n("<p>There are backup database files from previous versions "
	    "of KBabel. However, another version of KBabel (probably from KDE 3.1.1 or 3.1.2) "
	    "created a new database. As a result, your KBabel installation contains two versions "
	    "of database files. Unfortunatelly, the old and new version "
	    "can't be merged. You need to choose one of them.<br/><br/>"
	    "If you choose the old version, the new one will be removed. "
	    "If you choose the new version, the old database files will be left alone "
	    "and you need to remove them manually. Otherwise this message will be displayed "
	    "again (the old files are at $KDEHOME/share/apps/kbabeldict/dbsearchengine/*,old).</p>")
	    , i18n("Old database found"), i18n("Use &old database"), i18n("Use &new database")
	  ) == KMessageBox::Yes ) {
	    // remove the new files
	    QFile::remove( transFile );
	    QFile::remove( database+"/wordsindex."+lang+".db" );
	    QFile::remove( database+"/keysindex."+lang+".db" );
	    QFile::remove( database+"/catalogsinfo."+lang+".db" );
	    
	    // rename the old files
	    KIO::NetAccess::copy( transFile+",old", transFile );
	    KIO::NetAccess::copy( database+"/wordsindex."+lang+".db,old", database+"/wordsindex."+lang+".db" );
	    KIO::NetAccess::copy( database+"/keysindex."+lang+".db,old", database+"/keysindex."+lang+".db" );
	    KIO::NetAccess::copy( database+"/catalogsinfo."+lang+".db,old", database+"/catalogsinfo."+lang+".db" );

	    QFile::remove( transFile+",old" );
	    QFile::remove( database+"/wordsindex."+lang+".db,old" );
	    QFile::remove( database+"/keysindex."+lang+".db,old" );
	    QFile::remove( database+"/catalogsinfo."+lang+".db,old" );
	  }
      }
      else if ( oldstuff ) {
    	  // rename the old files
	    KIO::NetAccess::copy( transFile+",old", transFile );
	    KIO::NetAccess::copy( database+"/wordsindex."+lang+".db,old", database+"/wordsindex."+lang+".db" );
	    KIO::NetAccess::copy( database+"/keysindex."+lang+".db,old", database+"/keysindex."+lang+".db" );
	    KIO::NetAccess::copy( database+"/catalogsinfo."+lang+".db,old", database+"/catalogsinfo."+lang+".db" );

	    QFile::remove( transFile+",old" );
	    QFile::remove( database+"/wordsindex."+lang+".db,old" );
	    QFile::remove( database+"/keysindex."+lang+".db,old" );
	    QFile::remove( database+"/catalogsinfo."+lang+".db,old" );
      }
      
      dm= new DataBaseManager(database,lang,this,"Database manager");

      if(!dm->isOk())
      {
        if(asked || KMessageBox::questionYesNo(0
            ,i18n("Database files not found.\nDo you want to create them now?"))
            ==KMessageBox::Yes)
        {
           //fprintf(stderr,"SI\n");
           ret=dm->createDataBase(database,lang);
        }
        else
          ret=false;
      } 
      else ret=true;
  }

//Wrong errore hangdling

  if(ret)
   totalRecord=dm->count();
  else totalRecord=0;

  return ret;
}
  

/*
   Set if the research have to consider multiple spaces as a single one.
 */
 
void KDBSearchEngine::setNormalizeSpace(bool normalize)
{
  norm=normalize;
}


void KDBSearchEngine::setRemoveInternalComment(bool internalcomment)
{
  comm=internalcomment;
}

/*
   Set if the research have to be Case Sensitive or not
 */
 
void KDBSearchEngine::setCaseSensitive(bool sensitive)
{
  sens=sensitive;
}
/*
   Set the a string containing all char that must be ignored
   during the search.
 */
 
void KDBSearchEngine::setRemoveCharString(QString chartoremove)
{
  remchar=chartoremove;
}
/*
   Return true if there's a search in progress.
  */
bool KDBSearchEngine::isSearching() const
{
  return searching;
}




/*
   Add a search string in the list of the string to search for.
   Returns the ID of the string in the list.  
   Returns -1 if there is a problem (may be search in progress)
  */ 
 
int KDBSearchEngine::addSearchString(QString searchString, int rule)
{
  if(searching||scanInProgress) return -1;
  SearchEntry e;
  e.string=QString(searchString);
  e.rules=rule;
  searchStringList.append(e);
  return searchStringList.count();
}


/*
   Start the research in the database of all the string in the list

  */

bool KDBSearchEngine::startSearch(QString str)
{

 if(autoUpdate)
   {
    updateSettings();
   }


 int l1=0,l2=0;
 if(defSub1) l1=defLimit1;
 if(defSub2) l2=defLimit2;

return startSingleSearch(str,l1,l2);  


}

bool KDBSearchEngine::startSearchInTranslation(QString s)
{

 if(autoUpdate)
   {
    updateSettings();
   }


 int l1=0,l2=0;
 if(defSub1) l1=defLimit1;
 if(defSub2) l2=defLimit2;


 return startSingleSearch(s,l1,l2,true); 

}


bool KDBSearchEngine::openDb(bool noask=false)
{
  if(!dbOpened)
    {
      dbOpened=loadDatabase(dbname,noask);  //Try first to open it now
     if(!dbOpened) // Still not opened!!
         {
             hasError(i18n("Cannot open the database"));
             return false;
          }
    }
  
return true;  
}




bool KDBSearchEngine::messagesForPackage(const QString& package
                         , QValueList<Message>& resultList, QString& error)
{

  int count = 0;
  stopNow=false; // Remove dirty.
  Message m;

  if(searching )
  {
   error=i18n("Another search has already been started");
   return false;
  }

  if(scanInProgress)
  {
   error=i18n("Unable to search now: a PO file scan is in progress");
   return false;
  }


  if(!openDb()) {
   error=i18n("Unable to open the database");
   return false;
  }

   if(totalRecord<=0)
     {
     error=i18n("Database empty");
     return false;
     }

   int step=(totalRecord/30)+1;
   int ntra,nref;
   int req=dm->searchCatalogInfo(package);
   if(req==-1) {
    error=i18n("No entry for this package in the database.");
    return false;
   }
   DataBaseItem item;
   int i,h;
   kdDebug(0) << "looking for catalog " << req <<endl;
   
   progressStarts(i18n("Searching for %1 in database").arg(package));

   for(item = dm->firstItem();!item.isNull();item=dm->nextItem())
   {
      count++;	       
      if(count%step==0){
           emit progress(100*count/totalRecord);
           kapp->processEvents(100);
         }
      if(stopNow) {
	stopNow=false;
	searching=false;
	emit finished();
	return true;	// No error, stopped!
      }
      
      ntra=item.numTra;
      for(i=0;i<ntra;i++) {
       nref=item.translations[i].numRef;

          for(h=0;h<nref;h++){
            if(item.translations[i].infoRef[h]==req) {
    		m.msgid=item.key;
    		m.msgstr=item.translations[i].translation;
		resultList.append(m);      	
	      }
    	  }
      }	  
       
   }
   
   return true;
}

void KDBSearchEngine::repeat()
{
     
  int count = 0;
  stopNow=false; // Remove dirty.

  if(searching )
  {
    return;
  }

  if(scanInProgress)
  {
   return;
  }


  if(!openDb()) {
   return;
  }

   if(totalRecord<=0)
     {
     return;
     }

   int step=(totalRecord/30)+1;
   int ntra,nref;

   DataBaseItem item;
   int i,h,tot;

   int req=dm->searchCatalogInfo("kdelibs.po");
   if(req==-1) kdDebug(0) << "No kdelibs.po found!" << endl;


   QProgressDialog *pd=new QProgressDialog(i18n("Looking for repetitions"),i18n("Stop"),100);

   connect(this,SIGNAL(progress(int)),pd,SLOT(setProgress(int)));
   connect(this,SIGNAL(finished()),pd,SLOT(close()));
   connect(pd,SIGNAL(cancelled()),this,SLOT(stopSearch()));

   
   QString txt="// %1 repetitions, %2 translation(s)\ni18n(\"%3\");\n";
   QString id;
   int min;
   bool ok = false;
  
   min=QInputDialog::getInteger(i18n("Minimum repetition"),i18n("Insert the minimum number of repetitions for a string"),
				2,1,999999,1,&ok);

   if(!ok)
       return;

     pd->show();
  
   progressStarts(i18n("Searching repeated string"));
   
   static QTextEdit *mle=new QTextEdit();
   mle->clear();
   
   bool inlibs;

   for(item = dm->firstItem();!item.isNull();item=dm->nextItem())
   {
      count++;	       
      if(count%step==0){
           emit progress(100*count/totalRecord);
           kapp->processEvents(100);
         }
      if(stopNow) {
	stopNow=false;
	searching=false;
	emit finished();
	return;	// No error, stopped!
      }
      tot=0;
      inlibs=false;
      ntra=item.numTra;
      for(i=0;i<ntra;i++) {
       nref=item.translations[i].numRef;
          for(h=0;h<nref;h++)
            if(item.translations[i].infoRef[h]==req) inlibs=true;

	    tot+=nref;
    	  }
	
      if (tot>=min && !inlibs)
	{	
    	    id=item.key;		
	    id=id.replace(QRegExp("\\n"),"\"\n\"");    	    
	    mle->append(txt.arg(tot).arg(ntra).arg(id));
      
	}      
      }	  
       
   
     emit progress(100);
	emit finished();
    mle->resize(400,400);
    mle->show();
    
    delete pd;
   return;
}



bool KDBSearchEngine::startSearchNow(int searchmode)
{
  if(searchmode==-1) searchmode=mode;
  int count = 0;
  stopNow=false; // Remove dirty.
  clearResults();

 
  if(searching )
  {
   hasError(i18n("Another search has already been started"));
   return false;
  }

  if(scanInProgress)
  {
   hasError(i18n("Unable to search now: a PO file scan is in progress"));
   return false;
  }


  if(!openDb())
   return false;
 

   if(totalRecord<=0)
     {
     hasError(i18n("Database empty"));
     return false;
     }



  searching=true;

  emit started();

  bool allkey=(searchmode==MD_ALL_GOOD_KEYS);

  bool equal,contains,contained,regexp,intra;
  
  intra=searchmode & MD_IN_TRANSLATION;
  
  QString msgIdFound;
  QString msgId;
  QString msgStr;
  //QString msgIdRequested;
  SearchResult *aresult;
  TranslationInfo *adescription;
  SearchList searchList;
  int i,len,files,h;
  len=remchar.length();

  int n,m;//,word;
  QString  *id;

  QString mainRequest=searchStringList[0].string;


  SearchList::Iterator it,it1;
  QString *idMod;
  bool foundSomething=false;

  searchList=searchStringList;   //Create a copy and modify it
  if(!allkey)
  {
  for ( it=searchList.begin(); it != searchList.end(); ++it )
    {
      idMod=&((*it).string);
	int pos;
	for(i=0;i<len;i++){
	 while((pos=idMod->find(remchar.at(i)))!=-1)
	idMod->remove(pos,1);
	}

        if(comm)
	 idMod->replace(QRegExp("\\_\\:.*\\\\n"),"");  //Read it from catalog !!! (NOT ONLY HERE)


      if(norm) idMod->simplifyWhiteSpace();
      if(!sens) *idMod=idMod->upper();
    }

   }

  timeval now;
  gettimeofday(&now,NULL);
  //fprintf(stderr,"\n%ld.%ld\n",now.tv_sec,now.tv_usec);
  //double tim=1.0*now.tv_usec/1000000.0+now.tv_sec;
  int pos;



   DataBaseItem item;


  //Now we can browse the whole db or the "good keys"
 QValueList<KeyAndScore> goodkeys;
  int totalprogress;

  bool gk=(searchmode==MD_GOOD_KEYS)||allkey;
  int k=0;
  
    if(gk) {
     goodkeys=searchWords(mainRequest,thre,threorig,listmax);//FIX IT, mainReq?
     if( goodkeys.count() == 0 ) gk = false; // if no good keys, use the whole database
	}
   totalprogress=gk?goodkeys.count():totalRecord;
   int step=(totalprogress/30)+1;
	   
   for(item = gk?(dm->getItem(goodkeys[0])):(dm->firstItem());
	!item.isNull();
       item = gk?(dm->getItem(goodkeys[++k])):(dm->nextItem()))
   {


//   Emit progress, process event and check stop now
      if(count%step==0){
           emit progress(100*count/ /*QMAX(*/totalprogress/*,1)*/);
           kapp->processEvents(100);
         }
      if(stopNow) {
	stopNow=false;
	searching=false;
	emit finished();
	return true;	// No error, stopped!
      }

    //  fprintf(stderr,"%s\n",(const char *)item.key.utf8());
      msgIdFound=item.key;  //Check if this is OK with UTF8
    
//     searchmode && MD_IN_TRANSLATION)

      count++;



       msgId=msgIdFound;
    
     if(!allkey)
     {

	//Remove character in list of character to be ignored
	for(i=0;i<len;i++)
	 while((pos=msgId.find(remchar.at(i)))!=-1)
	msgId.remove(pos,1);

	//Remove context information from id found
        if(comm)
	 msgId.replace(QRegExp("\\_\\:.*\\\\n"),"");


      if(norm) msgId.simplifyWhiteSpace();
      if(!sens) msgId=msgId.upper();
      }
     


      it=searchList.begin();
      idMod=&((*it).string);
      bool foundExact=false;
      
      
      for ( it1=searchStringList.begin(); it1 != searchStringList.end(); it1++ )
	{

	  id=&((*it1).string);
	  uint nn=0;
	  do {
	    if(intra) {
	       msgId=item.translations[nn].translation;
	       if(!allkey) {
		   //Remove character in list of character to be ignored
		    for(i=0;i<len;i++)
	    	    while((pos=msgId.find(remchar.at(i)))!=-1)
			msgId.remove(pos,1);

		   //Remove context information from id found
    		   if(comm)
	               msgId.replace(QRegExp("\\_\\:.*\\\\n"),"");


	           if(norm) msgId.simplifyWhiteSpace();
                   if(!sens) msgId=msgId.upper();
              }
	
	       
	    }
	        
	    
	    int rules=(*it).rules;
  	  
	    if (rules & Equal) equal=(*idMod==msgId); else equal=false;
	  	  
	    if (rules & Contains) contains=idMod->contains(msgId); else contains=false;

   	    if (rules & Contained) contained=msgId.contains(*idMod); else contained=false;
    	      
	    if  (!foundExact && (rules & RegExp ))
		{
	          QRegExp reg(*idMod);
	          regexp=(reg.search(msgId)!=-1);
		} else regexp=false;
   	  nn++;
	  } while(intra && nn<item.numTra);
	
	  if(equal||contains||contained||regexp||allkey)
	    {

//	     kdDebug(0) << QString("Found") << endl;

	    
	      if(equal) foundExact=true;
	      
	      m=item.numTra;  //Translations found.

	      for(n=0;n<m;n++)
		{

		  foundSomething=true;


		  msgStr=item.translations[n].translation;

		  files=item.translations[n].numRef;

		  aresult=new SearchResult();
		  
		  results.setAutoDelete(true);
		if(!gk)
		  aresult->score=score(mainRequest,msgIdFound);
		 else
  		  aresult->score=goodkeys[k].score;	
		
		if(intra)
		  aresult->score=score(mainRequest,item.translations[n].translation);
		

		  SearchResult *s=0;
		  for(s=results.first();s!=0;s=results.next())
		   {
 		    if(s->score > aresult->score)
		    {
		     results.insert(results.at(),aresult);
		     break;
		    } 
			
		   }
		  if(s==0) //no break or empty list
	 	     results.append(aresult);


/*		  if(*id==msgIdFound)  //Put it first of the list
		  results.prepend(aresult);
		    else
		  results.append(aresult);
*/
		  aresult->requested=*id;
		  aresult->found=msgIdFound;
		  aresult->translation=msgStr;
		  aresult->descriptions.setAutoDelete(true);
		  for(h=0;h<files;h++)
		    {
	
		      aresult->descriptions.append(adescription=new TranslationInfo());
		      int rr=item.translations[n].infoRef[h];

    	              InfoItem info=dm->getCatalogInfo(rr);


		      adescription->location=info.catalogName;
		      adescription->translator=info.lastTranslator;
		      adescription->filePath=info.lastFullPath;	
		    }

		  emit numberOfResultsChanged(results.count());
          emit resultFound(aresult);

//		  if(*id==msgIdFound)  //Put it first of the list so th order change
		    emit  resultsReordered();



		}
	    }
	  //        idMod=searchList.next();
	  it++;
	  idMod=&((*it).string);
	}

     
    }
  gettimeofday(&now,NULL);
  //fprintf(stderr,"%ld.%ld\n",now.tv_sec,now.tv_usec);

  //fprintf(stderr,"Finish, %d (of %d) records in %f seconds!!\n",count,totalRecord, 1.0*now.tv_usec/1000000.0+now.tv_sec-tim);
  emit progress(100);
  emit finished();
  
  searching=false;
  return true;  	//foundSomething;
}
 
/* 
  Start a search for a single string
 */

bool KDBSearchEngine::startSingleSearch(QString searchString,
				     unsigned int pattern1Limit,unsigned int /*pattern2Limit*/,
				     bool inTranslation)
{
  /*
    Check Ret 
    value.
	*/

  unsigned int nw=0;
  int in=0,len=0;
  clearList();
  addSearchString(searchString,defRule);


	 
  QRegExp  reg("[a-zA-Z0-9_%"/*+remchar*/+regaddchar+"]+");
  while((in=reg.search(searchString,in+len))!=-1) {nw++;len = reg.matchedLength();}
  in=0;
  len=0;
 // fprintf(stderr,"asas %d\n",nw);
 
  if(mode==MD_ALL_GOOD_KEYS && !inTranslation)
	return startSearchNow();

	   
  
  if((nw<pattern1Limit) && (nw >1))
    for(unsigned int k=0;k<nw;k++)
      {
	in=reg.search(searchString,in+len);
	len = reg.matchedLength();
	QString regToAdd = searchString;
	regToAdd.replace(in,len,"[a-zA-Z0-9_%"+regaddchar+"]*");
	regToAdd.append("$");
	regToAdd.prepend("^");
//	fprintf(stderr,"%s",(const char *)regToAdd.local8Bit());
	addSearchString(regToAdd,RegExp);
      }
      
if(inTranslation)
  return startSearchNow(MD_IN_TRANSLATION);
 else
  return startSearchNow();

  return false;
  
}


/* 
  Start a search for a list of string
 */
 
//bool KDBSearchEngine::startListSearch(QPtrList<QString> searchStrList)
//{
  //  searchStringList=searchStrList;
//  return startSearchNow();
//}
/*
  Stop the current search
  */



void KDBSearchEngine::setLanguageCode(QString ll)
{
if(ll==lang) return;

lang=ll;
 if(dbOpened)  //if opened open it again with new code, what about close before open ?
   dbOpened=loadDatabase(dbname);

}

void KDBSearchEngine::setLanguage(QString languageCode, QString /*languageName*/)
{
setLanguageCode(languageCode);
}

void KDBSearchEngine::stopSearch()
{
  stopNow=true;
}


void KDBSearchEngine::clearList()
{
 searchStringList.clear();
}
bool  KDBSearchEngine::isReady() const
	{return IAmReady;}


void  KDBSearchEngine::saveSettings(KConfigBase *config)
{
// updateSettings();  //maybe with autoupdate
 KConfigGroupSaver cgs(config,"KDBSearchEngine");
 config->writeEntry("Filename",dbname);
 config->writeEntry("Language",lang);


 config->writeEntry("CaseSensitive",sens);
 config->writeEntry("Normalize",norm);
 config->writeEntry("RemoveContext",comm);

 config->writeEntry("Rules",defRule);
 config->writeEntry("Limit1",defLimit1);
 config->writeEntry("Limit2",defLimit2);
 config->writeEntry("Substitution1",defSub1);
 config->writeEntry("Substitution2",defSub2);

 config->writeEntry("RegExp",regaddchar);
 config->writeEntry("RemoveCharacter", remchar);

config->writeEntry("Threshold1",thre);
config->writeEntry("Threshold2",threorig);
config->writeEntry("ListMax",listmax);
config->writeEntry("Mode",mode);       
config->writeEntry("CommonThrs",commonthre);
config->writeEntry("ReturnNothing",retnot);

config->writeEntry("AutoAuthor",autoauthor);
config->writeEntry("AutoUp",autoup);
 
}


void  KDBSearchEngine::readSettings(KConfigBase *config)
{
 QString newName;

 KConfigGroupSaver cgs(config,"KDBSearchEngine");

 QString defaultLang;
 QString oldLang=lang;
 Defaults::Identity def;
 defaultLang = def.languageCode();
 lang=config->readEntry("Language",defaultLang);

 QString defaultDir;
 KStandardDirs * dirs = KGlobal::dirs();
 if(dirs)
 {
     defaultDir = dirs->saveLocation("data");
     if(defaultDir.right(1)!="/")
         defaultDir+="/";
     defaultDir += "kbabeldict/dbsearchengine";
 }
 
 newName=config->readEntry("Filename",defaultDir);

 if(newName!=dbname || oldLang!=lang)
   {
    dbname=newName;
    if(dbOpened)  //Reload only if it is opened
    dbOpened=loadDatabase(dbname);
   }

 sens=config->readBoolEntry("CaseSensitive",false);
 norm=config->readBoolEntry("Normalize",true);
 comm=config->readBoolEntry("RemoveContext",true);

 defRule=config->readNumEntry("Rules",1);
 defLimit1=config->readNumEntry("Limit1",20);
 defLimit2=config->readNumEntry("Limit2",8);

  thre=config->readNumEntry("Threshold1",50);
  threorig=config->readNumEntry("Threshold2",50);
  listmax=config->readNumEntry("ListMax",500);
  mode=config->readNumEntry("Mode",MD_GOOD_KEYS);    
 
 defSub1=config->readBoolEntry("Substitution1",true);
 defSub2=config->readBoolEntry("Substitution2",false);

 regaddchar=config->readEntry("RegExp");
 remchar=config->readEntry("RemoveCharacter","&.:");
 commonthre=config->readNumEntry("CommonThrs",300);
 retnot=config->readBoolEntry("ReturnNothing",false);
 autoauthor=config->readEntry("AutoAuthor");
 autoup=config->readBoolEntry("AutoUp",true);

 setSettings();
}

PrefWidget * KDBSearchEngine::preferencesWidget(QWidget *parent)
{

 pw =  new PreferencesWidget(parent);
 setSettings();
 connect(pw,SIGNAL(restoreNow()),this,SLOT(setSettings()));
 connect(pw,SIGNAL(applyNow()),this,SLOT(updateSettings()));
 connect(pw,SIGNAL(destroyed()),this,SLOT(prefDestr()));
 connect(pw->dbpw->scanPB_2,SIGNAL(clicked()),this,SLOT(scan()));
 connect(pw->dbpw->scanrecPB,SIGNAL(clicked()),this,SLOT(scanRecur()));
 connect(pw->dbpw->scanFilePB,SIGNAL(clicked()),this,SLOT(scanFile()));
 connect(pw->dbpw->repeatPB,SIGNAL(clicked()),this,SLOT(repeat()));


return pw;
}

void KDBSearchEngine::scanRecur()
{
if (scanInProgress) return;
updateSettings();

if(!openDb())
	return;
scanInProgress=true;
PoScanner *sca=new PoScanner(dm,this,"Po Scanner");
QString cvsdir;
cvsdir=KFileDialog::getExistingDirectory("",0,i18n("Select Directory You Want to Scan Recursively"));

if(cvsdir.isEmpty()) {scanInProgress=false; return;}
if(pw) {
connect(sca,SIGNAL(patternProgress(int)),pw->dbpw->totalPB,SLOT(setProgress(int)) );
connect(sca,SIGNAL(fileLoading(int)),pw->dbpw->loadingPB,SLOT(setProgress(int)));
connect(sca,SIGNAL(fileProgress(int)),pw->dbpw->processPB,SLOT(setProgress(int)));
}

connect(sca,SIGNAL(patternProgress(int)),SIGNAL(progress(int)) ); //Kbabel progress bar

connect(sca,SIGNAL(added(int)),pw,SLOT(setEntries(int)));
connect(sca,SIGNAL(filename(QString)),pw,SLOT(setName(QString)));
 

progressStarts(i18n("Scanning directory %1").arg(cvsdir));
connect(sca,SIGNAL(patternFinished()),SIGNAL(progressEnds()) );

sca->scanPattern(cvsdir,"*.po",true);
disconnect(this,SIGNAL(progress(int)) );
//disconnect(SIGNAL(patternStarted()),this,SIGNAL(started()) );
disconnect(this,SIGNAL(progressEnds()) );
if(pw) {
disconnect(pw->dbpw->totalPB,SLOT(setProgress(int)) );
disconnect(pw->dbpw->loadingPB,SLOT(setProgress(int)));
disconnect(pw->dbpw->processPB,SLOT(setProgress(int))); 
}
totalRecord=dm->count();

scanInProgress=false;
dm->sync();
delete sca;
}


void KDBSearchEngine::scan()
{
if (scanInProgress) return;
updateSettings();

if(!openDb())
	return;
scanInProgress=true;
PoScanner *sca=new PoScanner(dm,this,"Po Scanner");
QString cvsdir;

cvsdir=KFileDialog::getExistingDirectory("",0,i18n("Select Directory You Want to Scan"));
if(cvsdir.isEmpty()) {scanInProgress=false; return;}
if(pw){
connect(sca,SIGNAL(patternProgress(int)),pw->dbpw->totalPB,SLOT(setProgress(int)) );
connect(sca,SIGNAL(fileLoading(int)),pw->dbpw->loadingPB,SLOT(setProgress(int)));
connect(sca,SIGNAL(fileProgress(int)),pw->dbpw->processPB,SLOT(setProgress(int))); 
}
connect(sca,SIGNAL(patternProgress(int)),SIGNAL(progress(int)) );
progressStarts(i18n("Scanning directory %1").arg(cvsdir));
connect(sca,SIGNAL(patternFinished()),SIGNAL(progressEnds()) );

connect(sca,SIGNAL(added(int)),pw,SLOT(setEntries(int)));
connect(sca,SIGNAL(filename(QString)),pw,SLOT(setName(QString)));



sca->scanPattern(cvsdir,"*.po",false);

disconnect(this,SIGNAL(progress(int)) );
//disconnect(SIGNAL(patternStarted()),this,SIGNAL(started()) );
disconnect(this,SIGNAL(progressEnds()) );
if(pw){
disconnect(pw->dbpw->totalPB,SLOT(setProgress(int)) );
disconnect(pw->dbpw->loadingPB,SLOT(setProgress(int)));
disconnect(pw->dbpw->processPB,SLOT(setProgress(int)));
}
totalRecord=dm->count();

scanInProgress=false;

dm->sync();
delete sca;
}

void KDBSearchEngine::scanFile()
{
if (scanInProgress) return;
updateSettings();

if(!openDb())
	return;
scanInProgress=true;
PoScanner *sca=new PoScanner(dm,this,"Po Scanner");
QString cvsdir;
pw->dbpw->totalPB->setProgress(0);

cvsdir=KFileDialog::getOpenFileName("","*.po",0
        ,i18n("Select PO File to Scan"));
if(cvsdir.isEmpty()) {scanInProgress=false; return;}
if(pw){
connect(sca,SIGNAL(fileLoading(int)),pw->dbpw->loadingPB,SLOT(setProgress(int)));
connect(sca,SIGNAL(fileProgress(int)),pw->dbpw->processPB,SLOT(setProgress(int)));
}
connect(sca,SIGNAL(fileProgress(int)),SIGNAL(progress(int)) );
progressStarts(i18n("Scanning file %1").arg(directory(cvsdir,0)));
connect(sca,SIGNAL(fileFinished()),SIGNAL(progressEnds()) );

connect(sca,SIGNAL(added(int)),pw,SLOT(setEntries(int)));
connect(sca,SIGNAL(filename(QString)),pw,SLOT(setName(QString)));



sca->scanFile(cvsdir);

sca->disconnect(SIGNAL(fileProgress(int)),this,SIGNAL(progress(int)) );
//disconnect(SIGNAL(patternStarted()),this,SIGNAL(started()) );
sca->disconnect(SIGNAL(fileFinished()),this,SIGNAL(progressEnds()) );
if(pw)
{
disconnect(pw->dbpw->loadingPB,SLOT(setProgress(int)));
disconnect(pw->dbpw->processPB,SLOT(setProgress(int)));
}

totalRecord=dm->count();

scanInProgress=false;

dm->sync();
delete sca;
}

const KAboutData *KDBSearchEngine::about() const
{

	return DbSeFactory::instance()->aboutData();

}

QString  KDBSearchEngine::name() const
{
return i18n("Translation Database");
}

QString  KDBSearchEngine::id() const
{
return QString("KDBSearchEngine");
}

QString  KDBSearchEngine::lastError()
{
return lasterror;
}

void  KDBSearchEngine::prefDestr()
{
pw=0;
}


void  KDBSearchEngine::setSettings()
{

if(pw==0) return;

 pw->dbpw->dirInput->setURL(dbname);
 pw->dbpw->caseSensitiveCB->setChecked(sens);
 pw->dbpw->normalizeCB->setChecked(norm);
 pw->dbpw->removeContextCB->setChecked(comm);

 pw->dbpw->oneWordSubCB->setChecked(defSub1);
 pw->dbpw->twoWordSubCB->setChecked(defSub2);


 if(defRule==8) pw->dbpw->RegExpRB->setChecked(true);
  else
  {
   pw->dbpw->normalTextRB->setChecked(true);
  pw->dbpw->equalCB->setChecked( defRule & Equal );
   pw->dbpw->containsCB->setChecked( defRule & Contains );
   pw->dbpw->containedCB->setChecked( defRule & Contained );
  }
 
 pw->dbpw->oneWordSubSB->setValue(defLimit1);
 pw->dbpw->twoWordSubSB->setValue(defLimit2);

 pw->dbpw->maxSB->setValue(listmax);
 pw->dbpw->thresholdSL->setValue(thre);
 pw->dbpw->thresholdOrigSL->setValue(threorig);

  pw->dbpw->allRB->setChecked( mode==MD_ALL_DB);
  pw->dbpw->slistRB->setChecked( mode==MD_GOOD_KEYS);
  pw->dbpw->rlistRB->setChecked( mode==MD_ALL_GOOD_KEYS  );
	
  pw->dbpw->nothingCB->setChecked(retnot); 
  pw->dbpw->freqSB->setValue(commonthre);
 
 pw->dbpw->regExpLE->setText(regaddchar);
 pw->dbpw->ignoreLE->setText(remchar);
 
  pw->dbpw->authorLE->setText(autoauthor);
  pw->dbpw->autoAddCB_2->setChecked(autoup);

  

}


void  KDBSearchEngine::updateSettings()
{
if(pw==0) return;

 QString newName=pw->dbpw->dirInput->url();

 if(newName!=dbname)
   {
    kdDebug(0) << "Database changed" << endl;
    dbname=newName;
    if(dbOpened)
    dbOpened=loadDatabase(dbname);
   }

 sens=pw->dbpw->caseSensitiveCB->isChecked();
 norm=pw->dbpw->normalizeCB->isChecked();
 comm=pw->dbpw->removeContextCB->isChecked();

 int tmpRule=0;
 if(pw->dbpw->RegExpRB->isChecked())
  tmpRule=RegExp;
  else
  {
   if(pw->dbpw->equalCB->isChecked()) tmpRule+=Equal; 
   if(pw->dbpw->containsCB->isChecked()) tmpRule+=Contains;
   if(pw->dbpw->containedCB->isChecked()) tmpRule+=Contained;
  }

 defRule=tmpRule;
 
 defLimit1=pw->dbpw->oneWordSubSB->text().toInt();
 defLimit2=pw->dbpw->twoWordSubSB->text().toInt();
 defSub1=pw->dbpw->oneWordSubCB->isChecked();
 defSub2=pw->dbpw->twoWordSubCB->isChecked();

 listmax=pw->dbpw->maxSB->value();
 thre=pw->dbpw->thresholdSL->value();
 threorig=pw->dbpw->thresholdOrigSL->value();
 
if(pw->dbpw->allRB->isChecked()) mode=MD_ALL_DB;
 if(pw->dbpw->slistRB->isChecked()) mode=MD_GOOD_KEYS;
if(pw->dbpw->rlistRB->isChecked()) mode=MD_ALL_GOOD_KEYS;
 
 
 regaddchar=pw->dbpw->regExpLE->text();
 remchar=pw->dbpw->ignoreLE->text();
 
 retnot=pw->dbpw->nothingCB->isChecked();
 commonthre=pw->dbpw->freqSB->value();

 autoauthor=pw->dbpw->authorLE->text();
 autoup=pw->dbpw->autoAddCB_2->isChecked();

   
}

void  KDBSearchEngine::setLastError(QString er)
{
lasterror=er;
}

QString KDBSearchEngine::translate(const QString text)
{
if(!openDb())
      return QString::null;
/*
      	      
if(!dbOpened)
    {
    dbOpened=loadDatabase(dbname);  //Try first to open it now
    if(!dbOpened) // Still not opened!!
        {
         //emit anerror
	 hasError(i18n("Database not opened"));
         return QString::null;
       }
     }
*/

DataBaseItem dbit=dm->getItem(text);
 
if(dbit.isNull()) return QString::null;
if(dbit.numTra==1) return dbit.translations[0].translation;

uint32 n=dbit.numTra;
uint32 max=0,nmax=0;
for(uint32 i=0;i<n;i++)
 if(dbit.translations[i].numRef>max)
    {
	    nmax=i;
	    max= dbit.translations[i].numRef;
    }

return dbit.translations[nmax].translation;

   
}

QValueList<KeyAndScore> KDBSearchEngine::searchWords(QString phrase,int threshold,
						 int thresholdorig,uint32 max)
{
QValueList<QString> wordlist;

    if(!openDb())
    {
      QValueList<KeyAndScore> a; 
      return a;
    }

progressStarts(i18n("Searching words"));

QValueList<QString>::Iterator wlit;
wordlist=dm->wordsIn(phrase);
int nw=wordlist.count();
//QMemArray<WordItem> wi(nw);
QMemArray<uint32> numofloc(nw),currentloc(nw);
QMemArray<int> score(nw);
QMemArray<uint32 *> loc(nw),locorig(nw);
QValueList<uint32> resloc;
QValueList<int> resfound;
QValueList<KeyAndScore> keylist;
//wi.resize(wordlist.count());
int totalprogress=0;
int totrec=dm->count();
uint32 cthre=totrec*commonthre/10000;
int i=0,common=0;
for(wlit=wordlist.begin();wlit!=wordlist.end();++wlit)
 {
  WordItem wi=dm->getWordLocations(*wlit);
  if(!wi.notFound())   
  {
  if(wi.count<cthre)
       score[i]=1;
      else
      { score[i]=0; common++; }
  
	   locorig[i]=loc[i]=wi.locations;   
	   totalprogress+=numofloc[i]=wi.count;
	   currentloc[i]=0;
//	   score[i]=wi.score;
	   //wi[i]=WordItem(wi[i]);
	   //wi[i].locations.detach();
  	  i++;
//     }
 //   else
   //  common++;   
	  

  
  }
 }
bool cs=(common==nw);  //All words are common; 
if (totalprogress==0) totalprogress=1;
int step=totalprogress/30+1;
int count=0;
int thrs=(wordlist.count()*threshold)/100;
if(thrs<1) thrs=1;  // whole database ???

int tot=i;
//nt32 jmin=0;
int found;
uint32 min; //Big ?
bool empty=false;


while(!empty)
{
		       
 empty=true; 
found=retnot?common:0;
if(thrs<=found) thrs=found+1;  // whole database ???
 
 min=BIGNUMBER; 
 for(int j=0;j<tot;j++)
  if(cs || score[j]) {
     if(numofloc[j]>currentloc[j])   // Check if there's still something to do.
      empty=false;
         if(loc[j][0]<min)		    //Found the minimum head
	       min=loc[j][0];     
    
    
   } 
if(min!=BIGNUMBER)
 {
 for(int j=0;j<tot;j++)
    if(cs || score[j]) {
      if(loc[j][0]==min)	    //Count the heads, move forward
  	{
       	found++;
        count++;
	//check stopnow  here
	if(count%step==0)
	{
           emit progress(100*count/totalprogress);
            kapp->processEvents(100);
          }
	
	currentloc[j]++;
	 if(numofloc[j]==currentloc[j]) //End reached
	    loc[j][0]=BIGNUMBER;	//so set head to a big number
	   else   //Go on..
	   {
	     loc[j]++;
	   }	   
	}	
    }	//end of for
bool inserted=false;


    
if(found>=thrs)
  {
  //Words count in key.
  int nword=0;
  
  int in=0,len=0;
  QString keyst=dm->getKey(min);
  QRegExp  reg("[a-zA-Z0-9_%"/*+remchar*/+regaddchar+"]+");
  while((in=reg.search(keyst,in+len))!=-1) {nword++; len=reg.matchedLength();}

  if(found>=nword*thresholdorig/100)  //
   {
	
   	if(resfound.count()<=max || (*resfound.end() < found)) 
	if((*resfound.end())>= found)
	{
		inserted=true;
		resloc.append(min);
		 resfound.append(found);
				  
	}
	else
	for(uint32 j=0;j<resloc.count();j++)
     	{
		if(resfound[j]<found || (resfound[j]==found && 0) )  //Orig word
		{	
  		 resloc.insert(resloc.at(j),min);
		 resfound.insert(resfound.at(j),found);
		 inserted=true;
		 break;
		}
    	 }			
		if(!inserted)
		{
			resloc.append(min);
			resfound.append(found);
		}
     
      }
  }

 }

}
int nres=(resloc.count()<max)?resloc.count():max;

for(int j=0;j<nres;j++)
 {
  QString strkey=dm->getKey(resloc[j]);
  int stdscore=KDBSearchEngine::score(phrase,strkey);
  int sc=0;
  
  if(stdscore<99)
    {
    int in=0,len=0,nword=0;
    int remove=retnot?common:0;
     QRegExp  reg("[a-zA-Z0-9_%"/*+remchar*/+regaddchar+"]+");
     while((in=reg.search(strkey,in+len))!=-1) {nword++;len=reg.matchedLength();}

//    kdDebug(0) << nword << "NWORD  " << resfound[j] << "FOUND  " 
//    << resfound[j]-remove << "REAL  " << remove << "to be remove   " << endl; 
    if(nword<1) nword=1;
    sc=  70-resfound[j]*70/nw+abs(nword-(resfound[j]-remove))*30/nword+2;
    sc=100-sc;
//    kdDebug(0) <<" Score : " << sc << endl; 

    }else
    sc=stdscore;
    
  KeyAndScore key(strkey,sc);
 
//  kdDebug(0) << (QString) key << " [" << key.score << "]" << endl;
  keylist.append(key);
 }

//kdDebug(0) << "Here!" << endl;

for(int j=0;j<tot;j++)
 {
	 free(locorig[j]);
 }
progressStarts(i18n("Process output")); 
return keylist;
}

void KDBSearchEngine::stringChanged( QString orig, QString translated
		                      , QString /*description*/)
{
//author is KBABEL ????
//#warning uncomment and FIXME
	//done

if(autoup)
{
/* if(!dbOpened)
 {
  dm= new DataBaseManager(dbname,lang,this,"Database manager");
  if(dm->isOk())
   dbOpened=true;
  else
  {
   dbOpened=false;
   delete dm;
   dm=0;
  } 
 }*/
 
if(openDb(true))  //true= no ask
 {

	dm->putNewTranslation(orig,translated,dm->catalogRef(directory(edited,0),autoauthor,edited));
 //kdDebug(0) << "Changed " << orig << " " << translated << endl; 
      dm->sync();
 }
}

}

void KDBSearchEngine::setEditedFile(QString file)
{

	edited=file;//kdDebug(0) << edited << endl;
}
    
KeyAndScore::KeyAndScore(const QString &a,int sc)
: QString(a)
{
score=sc;
}
KeyAndScore::KeyAndScore()
: QString()
{
score=0;
}

#include "KDBSearchEngine.moc"

