/****************************************************************************
                             Hyper's CD Catalog 
		A multiplatform qt and xml based catalog program
  
 Author    : Peter Deak (hyperr@freemail.hu)
 License   : GPL
 Copyright : (C) 2003 Peter Deak
****************************************************************************/

#include "find.h"

#include <qvariant.h>
#include <qcheckbox.h>
#include <qcombobox.h>
#include <qframe.h>
#include <qheader.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qlistview.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <qtooltip.h>
#include <qwhatsthis.h>
#include <qimage.h>
#include <qpixmap.h>
#include <qmessagebox.h>

#include <pcre.h>
#include <string.h>
#include <ctype.h>

#include "dbase.h"
#include "mainwidget.h"
#include "wdbfile.h"
#include "guibase.h"
#include "adddialog.h"
#include "config.h"
#include "icons.h"

findDialog::findDialog( CdCatMainWidget* parent, const char* name, bool modal, WFlags fl )
    : QDialog( parent, name, modal, fl )

{
    if ( !name )
	setName( "findDialog" );
      setIcon(*get_t_find_icon());
      
      mainw = parent;

      setSizeGripEnabled( TRUE );
      FindDialogBaseLayout = new QGridLayout( this, 1, 1, 11, 6, "FindDialogBaseLayout");

//    QSpacerItem* spacer = new QSpacerItem( 210, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_2 = new QSpacerItem( 240, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_3 = new QSpacerItem( 240, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_4 = new QSpacerItem( 200, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_5 = new QSpacerItem( 36, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_6 = new QSpacerItem( 190, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_7 = new QSpacerItem( 200, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_8 = new QSpacerItem( 190, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_9 = new QSpacerItem( 190, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
      QSpacerItem* spacer_10 = new QSpacerItem( 200, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );


      layout40 = new QVBoxLayout( 0, 0, 6, "layout40");
      layout39 = new QGridLayout( 0, 1, 1, 0, 6, "layout39");
      layout36 = new QGridLayout( 0, 1, 1, 0, 6, "layout36");
      layout37 = new QGridLayout( 0, 1, 1, 0, 6, "layout37");
      layout31 = new QVBoxLayout( 0, 0, 6, "layout31");
      layout17 = new QHBoxLayout( 0, 0, 6, "layout17");
      layout15 = new QGridLayout( 0, 1, 1, 0, 6, "layout15");
      layout30 = new QHBoxLayout( 0, 0, 6, "layout30");
      layout16 = new QGridLayout( 0, 1, 1, 0, 6, "layout16");

      leText = new QLineEdit( this, "leText" );

      cbCasesens = new QCheckBox( this, "cbCasesens" );
      cbEasy = new QCheckBox( this, "cbEasy" );

      cbFilename = new QCheckBox( this, "cbFilename" );
      cbDirname  = new QCheckBox( this, "cbDirname" );
      cbComment  = new QCheckBox( this, "cbComment" );
      cbContent  = new QCheckBox( this, "cbContent" );

      cbArtist = new QCheckBox( this, "cbArtist" );
      cbTitle = new QCheckBox( this, "cbTitle" );
      cbAlbum = new QCheckBox( this, "cbAlbum" );
      cbTcomm = new QCheckBox( this, "cbTcomm" );

      cbOwner = new QComboBox( FALSE, this, "cbOwner" );
      cbOwner->setMinimumSize( QSize( 0, 0 ) );

      cbSin = new QComboBox( FALSE, this, "cbSin" );

      buttonOk = new QPushButton( this, "buttonOk" );
      buttonOk->setAutoDefault( TRUE );
      buttonOk->setDefault( TRUE );
      buttonOk->setMinimumWidth(80);

      buttonCancel = new QPushButton( this, "buttonCancel" );
      buttonCancel->setAutoDefault( TRUE );
      buttonCancel->setMinimumWidth(80);

      resultsl = new QListView( this, "resultsl" );
      resultsl->addColumn( tr( "Name" ) );
      resultsl->addColumn( tr( "Type" ) );
      resultsl->addColumn( tr( "Media" ) );
      resultsl->addColumn( tr( "Path" ) );
      resultsl->addColumn( tr( "Modification" ) );

      buttonClose = new QPushButton( this, "buttonClose" );

      textLabel3 = new QLabel( this, "textLabel3" );
      textLabel1 = new QLabel( this, "textLabel1" );
      textLabel2 = new QLabel( this, "textLabel2" );
      textLabel5 = new QLabel( this, "textLabel5" );

   /* saved ops: */

      cbTcomm    -> setChecked( mainw->cconfig->find_mco );
      cbCasesens -> setChecked( mainw->cconfig->find_cs  );
      cbArtist   -> setChecked( mainw->cconfig->find_mar );
      cbAlbum    -> setChecked( mainw->cconfig->find_mal );
      cbComment  -> setChecked( mainw->cconfig->find_co  );
      cbTitle    -> setChecked( mainw->cconfig->find_mti );
      cbEasy     -> setChecked( mainw->cconfig->find_em  );
      cbFilename -> setChecked( mainw->cconfig->find_fi  );
      cbDirname  -> setChecked( mainw->cconfig->find_di  );
      cbContent  -> setChecked( mainw->cconfig->find_ct  );

   /* layouts:   */
      layout36->addWidget( cbCasesens, 1, 0 );
      layout36->addWidget( cbDirname , 2, 0 );
      layout36->addWidget( cbFilename, 3, 0 );
      layout36->addWidget( cbComment , 4, 0 );            
      layout36->addWidget( cbContent , 5, 0 );
      layout36->addWidget( cbEasy  , 1, 1 );      
      layout36->addWidget( cbArtist, 2, 1 );
      layout36->addWidget( cbTitle , 3, 1 );
      layout36->addWidget( cbAlbum , 4, 1 );
      layout36->addWidget( cbTcomm , 5, 1 );
      layout36->addMultiCellWidget( leText, 0, 0, 0, 1 );

      layout37->addWidget( cbOwner, 3, 1 );
      layout37->addWidget( cbSin, 2, 1 );
      layout37->addMultiCellWidget( textLabel3, 0, 0, 0, 1 );
      layout37->addWidget( textLabel1, 2, 0 );
      layout37->addWidget( textLabel2, 3, 0 );
      layout37->addMultiCell( spacer_3, 1, 1, 0, 1 );

      layout39->addMultiCellLayout( layout36, 0, 1, 1, 1 );
      layout39->addItem( spacer_2, 1, 0 );
      layout39->addLayout( layout37, 0, 0 );

      layout15->addWidget( buttonOk, 0, 0 );
      layout15->addItem( spacer_5, 0, 1 );
      layout15->addWidget( buttonCancel, 0, 2 );

      layout17->addItem ( spacer_6 );
      layout17->addLayout( layout15 );
      layout17->addItem( spacer_4 );

      layout30->addItem( spacer_7 );
      layout30->addWidget( textLabel5 );
      layout30->addItem( spacer_8 );

      layout16->addItem( spacer_9, 0, 2 );
      layout16->addWidget( buttonClose, 0, 1 );
      layout16->addItem( spacer_10, 0, 0 );

      layout31->addLayout( layout17 );
      layout31->addLayout( layout30 );
      layout31->addWidget( resultsl );
      layout31->addLayout( layout16 );

      layout40->addLayout( layout39 );
      layout40->addLayout( layout31 );

      FindDialogBaseLayout->addLayout( layout40, 0, 0 );

    resize( QSize(mainw->cconfig->findWidth,mainw->cconfig->findHeight).expandedTo(minimumSizeHint()));
    move(mainw->cconfig->findX,mainw->cconfig->findY);
    clearWState( WState_Polished );

    languageChange();
    
    fillCBox();
    connect(buttonCancel,SIGNAL(clicked()),this,SLOT(cancele()));
    connect(buttonOk,SIGNAL(clicked()),this,SLOT(seeke()));
    connect(buttonClose,SIGNAL(clicked()),this,SLOT(closee()));
    connect(resultsl,SIGNAL(currentChanged(QListViewItem *)),this,SLOT(select(QListViewItem *)));
    connect(resultsl,SIGNAL(clicked(QListViewItem *)),this,SLOT(select(QListViewItem *)));

    setTabOrder(leText,buttonOk);
    leText->setFocus();
}

/*
 *  Destroys the object and frees any allocated resources
 */
findDialog::~findDialog()
{
    // no need to delete child widgets, Qt does it all for us
}
/***************************************************************************/  
void findDialog::languageChange()
{
     setCaption( tr( "Search in the database..." ) );
     textLabel1->setText( tr( "Seek in:" ) );
     textLabel2->setText( tr( "Owner:" ) );
     resultsl->header()->setLabel( 0, tr( "Name" ) );
     resultsl->header()->setLabel( 1, tr( "Type" ) );
     resultsl->header()->setLabel( 2, tr( "Media" ) );
     resultsl->header()->setLabel( 3, tr( "Path" ) );
     resultsl->header()->setLabel( 4, tr( "Modification" ) );
     textLabel3->setText( tr( "Find:" ) );
     cbFilename->setText( tr( "File name" ) );
     cbAlbum->setText(    tr( "mp3-tag Album" ) );
     cbArtist->setText(   tr( "mp3-tag Artist" ) );
     cbTcomm->setText(    tr( "mp3-tag Comment" ) );
     cbDirname->setText(  tr( "Media / Directory name" ) );
     cbTitle->setText(    tr( "mp3-tag Title" ) );
     cbComment->setText(  tr( "Comment" ) );
     cbContent->setText(  tr( "Content" ) );
     buttonOk->setText(   tr( "&OK" ) );
#ifndef _WIN32
     buttonOk->setAccel( QKeySequence( QString::null ) );
#endif
     buttonCancel->setText( tr( "&Cancel" ) );
#ifndef _WIN32
     buttonCancel->setAccel( QKeySequence( QString::null ) );
#endif
     textLabel5->setText( tr( "Results" ) );
     buttonClose->setText( tr( "Close / Go to selected" ) );
     cbCasesens->setText( tr( "Case sensitive" ) );
     cbEasy->setText( tr( "Use easy matching instead regex" ) );
     resultsl->clear();
}
/***************************************************************************/
int findDialog::saveState(void)
 {
  mainw->cconfig->find_em    = cbEasy->isChecked();
  mainw->cconfig->find_cs    = cbCasesens->isChecked();
  mainw->cconfig->find_di    = cbDirname->isChecked();
  mainw->cconfig->find_fi    = cbFilename->isChecked();
  mainw->cconfig->find_co    = cbComment->isChecked();
  mainw->cconfig->find_ct    = cbContent->isChecked();
  mainw->cconfig->find_mar   = cbArtist->isChecked();
  mainw->cconfig->find_mti   = cbTitle->isChecked();
  mainw->cconfig->find_mco   = cbTcomm->isChecked();
  mainw->cconfig->find_mal   = cbAlbum->isChecked();
  mainw->cconfig->findX      = x();
  mainw->cconfig->findY      = y();
  mainw->cconfig->findWidth  = width();
  mainw->cconfig->findHeight = height();
  mainw->cconfig->writeConfig();
  
  return 0;
 }
/***************************************************************************/   
int findDialog::fillCBox(void)
 {
  char ow[256];
  int  i,f,c;
  Node *tmp=mainw->db->getRootNode();

  if(tmp == NULL) return 0;

  cbOwner->clear();
  cbOwner->insertItem(tr("All/Everybody"),0);
  cbOwner->insertItem(((DBCatalog *)(tmp->data))->owner);
  
  cbSin  ->clear();
  cbSin  ->insertItem(tr("All media"),0);
  
  tmp=tmp->child; //Jump to the first media
  while(tmp != NULL)
   {
    if(tmp->type != HC_MEDIA) 
     {
      tmp=tmp->next;
      continue;
     }
     
    cbSin  ->insertItem(tmp->getNameOf());
    strcpy(ow,((DBMedia *)(tmp->data))->owner);
    c = cbOwner->count();
    for(i=0,f=1;c>i;i++)
      if(strcmp(ow,(const char *)cbOwner->text(i)) == 0) 
        f=0;
    if(f)
     cbOwner->insertItem(ow);    
    tmp=tmp->next;
   }
  return 0;  
 }
/***************************************************************************/   
int findDialog::select(QListViewItem *i)
 {
  if(i == NULL)
     return 0;

  char loc[1024];
  
  if(i->text(3).isEmpty()) //Not a real result ("There is no matching" label)
    return 0;
    
  strcpy(loc,i->text(3));
  mainw->guis->updateListFromNode(
         (mainw->guis->getNodeFromFullName(mainw->db->getRootNode(),QString(loc+2)))); 
  for(QListViewItemIterator it=mainw->listView->firstChild();it.current();it++)
   {
     if(strcmp((it.current())->text(0),i->text(0)) ==0)
        mainw->listView->setCurrentItem(it.current());       
   }
  mainw->listView->curr_vis();  
  mainw->listView->repaint();  
  return 0;
 }
/***************************************************************************/  
void findDialog::closeEvent(QCloseEvent *ce)
 {
  saveState();
  QWidget::closeEvent(ce);
 }

/***************************************************************************/
int findDialog::closee(void)
 {
  close ();
  return 0;
 }
/***************************************************************************/
int findDialog::cancele(void)
 {

  close();
  return 0;
 }
int findDialog::seeke(void)
 {
  seekEngine *se;

  if(mainw == NULL && mainw->db == NULL) 
    return 0;
  
  if((leText->text()).isEmpty()) 
    return 0;

  se = new seekEngine(this);
  se->start_seek();
  delete se;
  return 0;
 }
/***************************************************************************

  Seek engine Class

***************************************************************************/

seekEngine::seekEngine(findDialog *fdp)
 {
  fd   = fdp;
  patt = new char[2048];
 }
/***************************************************************************/
seekEngine::~seekEngine(void)
 {
  delete [] patt;
 }
/***************************************************************************/ 
int seekEngine::start_seek(void)
 {
 /*get the pattern*/
  strcpy(patt,(const char *)fd->leText->text());
     
  /*recode the pattern /easy/  put ^$ and ? -> .  * -> .* */
  if(fd->cbEasy->isChecked())
     easyFormConversion(patt);

  /*recode the pattern /Case sens/ lok -> [l|L][o|O][k|K] */
  if(!fd->cbCasesens->isChecked())
     caseSensConversion(patt);

 //fprintf(stderr,"The complete pattern is \"%s\"\n",patt);

  re = pcre_compile(patt,0,&error,&errptr,NULL);

  if(re == NULL)
   {
    sprintf(patt,"%s: %d",error,errptr);
    QMessageBox::warning(fd,tr("Error in the pattern:"),patt,tr("Ok"),0);
    return 1;
   }

  hints = pcre_study(re,0,&error);

  if(error != NULL)
   {
    QMessageBox::warning(fd,tr("Error in the pattern:"),error,tr("Ok"),0);
    return 1;
   }
  
  pww=new PWw(fd);
  pww->refreshTime=200;
  progress(pww);
  
  fd->resultsl->clear();
  founded = 0;
   
  dirname  = fd->cbDirname ->isChecked();
  filename = fd->cbFilename->isChecked();
  comment  = fd->cbComment ->isChecked();
  tartist  = fd->cbArtist  ->isChecked();
  ttitle   = fd->cbTitle   ->isChecked();
  tcomment = fd->cbTcomm   ->isChecked();
  talbum   = fd->cbAlbum   ->isChecked();
  content  = fd->cbContent ->isChecked();
  
  allmedia = false;
  allowner = false;

  strcpy(media,(const char *)(fd->cbSin  ->currentText()));
  strcpy(owner,(const char *)(fd->cbOwner->currentText()));

  if(0 == fd->cbOwner->currentItem())   allowner = true;
  if(0 == fd->cbSin  ->currentItem())   allmedia = true;

  progress(pww);    
  /*seek...*/
  analyzeNode(fd->mainw->db->getRootNode());

  if(founded == 0)
  fd->resultsl->insertItem(new QListViewItem(fd->resultsl,tr("There is no matching.")));

  progress(pww);  
  pww->end();
  delete pww;
  return 0;
 }
/***************************************************************************/ 
int seekEngine::analyzeNode(Node *n,Node *pa)
 {
  
   if(n == NULL) return 0; 
   switch(n->type)
    {
       case HC_CATALOG:
           analyzeNode(n->child);
           return 0;
       
       case HC_MEDIA:
           progress(pww);
          
	  //It is necessary to analyze this media node? /Owner/Media/
	   if((allmedia || (strcmp(media,n->getNameOf())==0)) &&
              (allowner || (strcmp(owner,((DBMedia *)(n->data))->owner)==0))) 
	       {
	           if(dirname)  
	            if(matchIt(n->getNameOf()))
                     {
	              putNodeToList(n);
		      analyzeNode(n->child);    
  	              analyzeNode(n->next);    
                      return 0;
	            }
                   if(comment)
	             if(matchIt(((DBMedia *)(n->data))->comment))
	              putNodeToList(n);

                  analyzeNode(n->child);	   
	       }	  
           analyzeNode(n->next );	    	   
           return 0;

       case HC_CATLNK:
	   analyzeNode(n->next);       
           return 0;
	   
       case HC_DIRECTORY:
           progress(pww);
         
	   if(dirname)  
	     if(matchIt(n->getNameOf()))
               {
	        putNodeToList(n);
		analyzeNode(n->child);    
  	        analyzeNode(n->next);    
                return 0;
	       }
	       
           if(comment)
	     if(matchIt(((DBDirectory *)(n->data))->comment))
	        putNodeToList(n);
	       
	   analyzeNode(n->child);    
	   analyzeNode(n->next);    
           return 0;
	   
       case HC_FILE:
           if(filename) 
	     if(matchIt(n->getNameOf()))
	      {
               putNodeToList(n);
  	       analyzeNode(n->next);    
               return 0;
	      }
	   if(comment)
	     if(matchIt(((DBFile *)(n->data))->comment))
	      {
               putNodeToList(n);
	       analyzeNode(n->next);    
               return 0;

	      }	
	   analyzeNode(((DBFile *)(n->data))->prop,n);
	   analyzeNode(n->next);    
           return 0;
	   
       case HC_MP3TAG:
           if(tartist)
	     if(matchIt(((DBMp3Tag *)(n->data))->artist))
	       {
	        putNodeToList(pa);
		return 0;
	       }
           if(ttitle)
	     if(matchIt(((DBMp3Tag *)(n->data))->title))
	       {
	        putNodeToList(pa);
		return 0;
	       }
           if(talbum)
	     if(matchIt(((DBMp3Tag *)(n->data))->album))
	       {
	        putNodeToList(pa);
		return 0;
	       }
           if(tcomment)
	     if(matchIt(((DBMp3Tag *)(n->data))->comment))
	       {
	        putNodeToList(pa);
		return 0;
	       }
 
	   analyzeNode(n->next,pa);       
           return 0;
       case HC_CONTENT:
           if(content)
	     if(matchIt((const char *)((DBContent *)(n->data))->bytes))
	       {
	        putNodeToList(pa);
		return 0;
	       }
	   analyzeNode(n->next,pa);       
           return 0;

    }
  return -1;
 } 

int  seekEngine::matchIt(const char *txt)
 {
  if(txt == NULL) return 0;

  int match = pcre_exec(re,hints,txt,strlen(txt),0,0,offsets,99);
  if(match == 1) return 1;
  return 0;
 }

void seekEngine::putNodeToList(Node *n)
 {
  
  Node *tmp;
  char type[30];
  char media[128];
  char mod[30];
  
  if(n == NULL) return;
  
  switch(n->type)
   {
    case HC_MEDIA:     strcpy(type,(const char *)tr("media"));
                       strcpy(mod,((DBMedia *)(n->data))->modification);    
        break;
   
    case HC_DIRECTORY: strcpy(type,(const char *)tr("dir"));
                       strcpy(mod,((DBDirectory *)(n->data))->modification);    
        break;
    case HC_FILE:      sprintf(type,"%s/ %.1f%s",(const char *)tr("file"),
                                ((DBFile *)(n->data))->size,
                                getSType(((DBFile *)(n->data))->sizeType));
                       strcpy(mod,((DBFile *)(n->data))->modification);    				
	break;
    default:           strcpy(type,(const char *)tr("error")); 
        break;
   }
  tmp=n;
  while(tmp->type != HC_MEDIA)
   tmp=tmp->parent;
  
  sprintf(media,"%s/%d",tmp->getNameOf(),((DBMedia *)(tmp->data))->number);      
  
  fd->resultsl->insertItem(new QListViewItem(fd->resultsl,
                                            n->getNameOf(),
					    type,
					    media,
					    n->getFullPath(),
					    mod
                                           ));
  progress(pww);					   
  founded++;
 }

/***************************************************************************/ 

