/***************************************************************************
    date                 : Mar 30 2007
    version              : 0.24
    copyright            : (C) 2004-2007 by Holger Danielsson
    email                : holger.danielsson@versanet.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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "configcodecompletion.h"

#include <kdialog.h>
#include <tdestandarddirs.h>
#include <tdemessagebox.h>
#include <tdelocale.h>

#include <tqlayout.h>
#include <tqtabwidget.h>
#include <tqgroupbox.h>
#include <tqvgroupbox.h>
#include <tqbuttongroup.h>
#include <tqlabel.h>
#include <tqspinbox.h>
#include <tqframe.h>
#include <tqwhatsthis.h>
#include <tqstringlist.h>
#include <tqfileinfo.h>
#include <tqdir.h>

#include "kilelistselector.h"
#include "kileconfig.h"
#include "kilelogwidget.h"
#include "kiletool_enums.h"

ConfigCodeCompletion::ConfigCodeCompletion(TDEConfig *config, KileWidget::LogMsg *logwidget, TQWidget *parent, const char *name )
   : TQWidget(parent,name), m_config(config), m_logwidget(logwidget)
{
   // Layout
    TQVBoxLayout *vbox = new TQVBoxLayout(this, 5,KDialog::spacingHint() );

   // Groupbox with TabDialog and two button
   TQGroupBox *gb_tab= new TQGroupBox(i18n("Complete Modes"), this );
   TQGridLayout *grid_tab = new TQGridLayout( gb_tab, 2,1, 12,8, "" );
   grid_tab->addRowSpacing( 0, 12 );

	// create TabWidget
	tab = new TQTabWidget(gb_tab);

	// add three pages: Tex/Latex, Dictionary, Abbreviation
	addPage(tab, TexPage, i18n("TeX/LaTeX"), "text-x-tex");
	addPage(tab, DictionaryPage, i18n("Dictionary"), "dictionary");
	addPage(tab, AbbreviationPage, i18n("Abbreviation"), "abbreviation");

   // add two centered button
   add = new KPushButton(i18n("Add..."),gb_tab);
   remove = new KPushButton(i18n("Remove"),gb_tab);

   grid_tab->addMultiCellWidget(tab,1,1,0,1);
   grid_tab->addWidget(add,2,0,TQt::AlignRight);
   grid_tab->addWidget(remove,2,1,TQt::AlignLeft);

	// below: OptionBox
	TQButtonGroup *bg_options = new TQButtonGroup( this, "bgOptions" );
	bg_options->setColumnLayout(0, TQt::Vertical );
	bg_options->layout()->setSpacing( 6 );
	bg_options->layout()->setMargin( 11 );
	TQGridLayout *bg_optionsLayout = new TQGridLayout( bg_options->layout() );
	bg_optionsLayout->setAlignment( TQt::AlignTop );

	cb_setcursor = new TQCheckBox(i18n("Place cursor"),bg_options);
	cb_setbullets = new TQCheckBox(i18n("Insert bullets"),bg_options);
	cb_closeenv = new TQCheckBox(i18n("Close environments"),bg_options);
	cb_usecomplete = new TQCheckBox(i18n("Use complete"),bg_options);
	cb_autocomplete = new TQCheckBox(i18n("Auto completion (LaTeX)"),bg_options);
	lb_latexthreshold = new TQLabel(i18n("Threshold:"),bg_options);
	sp_latexthreshold = new TQSpinBox(1,9,1,bg_options);
	TQLabel *lb_latexletters = new TQLabel(i18n("letters"),bg_options);
	cb_autocompletetext = new TQCheckBox(i18n("Auto completion (text)"),bg_options);
	lb_textthreshold = new TQLabel(i18n("Threshold:"),bg_options);
	sp_textthreshold = new TQSpinBox(1,9,1,bg_options);
	TQLabel *lb_textletters = new TQLabel(i18n("letters"),bg_options);
	cb_showabbrevview = new TQCheckBox(i18n("Show abbreviations"),bg_options);
	cb_autocompleteabbrev = new TQCheckBox(i18n("Auto completion (abbrev.)"),bg_options);
	cb_citeoutofbraces = new TQCheckBox(i18n("Move out of braces (citation keylists)"),bg_options);

	bg_optionsLayout->addWidget(cb_setcursor,0,0);
	bg_optionsLayout->addWidget(cb_setbullets,1,0);
	bg_optionsLayout->addWidget(cb_closeenv,2,0);
	bg_optionsLayout->addWidget(cb_showabbrevview,3,0);
	bg_optionsLayout->addWidget(cb_usecomplete,0,2);
	bg_optionsLayout->addWidget(cb_autocomplete,1,2);
	bg_optionsLayout->addWidget(lb_latexthreshold,1,4);
	bg_optionsLayout->addWidget(sp_latexthreshold,1,6);
	bg_optionsLayout->addWidget(lb_latexletters,1,7);
	bg_optionsLayout->addWidget(cb_autocompletetext,2,2);
	bg_optionsLayout->addWidget(lb_textthreshold,2,4);
	bg_optionsLayout->addWidget(sp_textthreshold,2,6);
	bg_optionsLayout->addWidget(lb_textletters,2,7);
	bg_optionsLayout->addWidget(cb_autocompleteabbrev,3,2);
	bg_optionsLayout->addMultiCellWidget(cb_citeoutofbraces,4,4,0,7);

	// tune layout
	bg_optionsLayout->setColSpacing(1,20);
	bg_optionsLayout->setColSpacing(3,12);
	bg_optionsLayout->setColSpacing(5,8);
	bg_optionsLayout->setColStretch(7,1); 
	
	TQWhatsThis::add(cb_setcursor,i18n("Try to place the cursor."));
	TQWhatsThis::add(cb_setbullets,i18n("Insert bullets, where the user must input data."));
	TQWhatsThis::add(cb_closeenv,i18n("Also close an environment, when an opening command is inserted."));
	TQWhatsThis::add(cb_usecomplete,i18n("Enable components of word completion."));
	TQWhatsThis::add(cb_autocomplete,i18n("Directional or popup-based completion with TeX/LaTeX commands, which are given in all selected word completion lists. This mode can only be selected, if no other plugin for autocompletion is active."));
	TQWhatsThis::add(cb_autocompletetext,i18n("Directional or popup-based completion from words in the current document. This mode can only be selected, if no other plugin for autocompletion is active."));
	TQWhatsThis::add(sp_latexthreshold,i18n("Automatically show a completion list of TeX/LaTeX commands, when the word has this length."));
	TQWhatsThis::add(sp_textthreshold,i18n("Automatically show a completion list, when the word has this length."));
	TQWhatsThis::add(cb_citeoutofbraces,i18n("Move cursor out of braces after selecting from a citation keylist."));

	// bottom: warning
	TQLabel *lb_automodes = new TQLabel(i18n("Warning: all autocompletion modes will be disabled, if you enable KTextEditor plugin word completion."),this);
	
	// add OptionBox and TabDialog into the layout
	vbox->addWidget(gb_tab);
	vbox->addWidget(bg_options);
	vbox->addWidget(lb_automodes);
	vbox->addStretch();

   connect(tab,TQ_SIGNAL(currentChanged(TQWidget*)),this,TQ_SLOT(showPage(TQWidget*)));
   connect(add,TQ_SIGNAL(clicked()),this,TQ_SLOT(addClicked()));
   connect(remove,TQ_SIGNAL(clicked()),this,TQ_SLOT(removeClicked()));

   // justify height
   TQCheckListItem *item = new TQCheckListItem(m_listview[AbbreviationPage], "Test", TQCheckListItem::CheckBox);
   int h = 6*(item->height()+1) + 1;
 	for ( uint i=TexPage; i<NumPages; ++i )
		m_listview[i]->setFixedHeight(h);
   delete item;

	// find resource directories for cwl files
	getCwlDirs();
}

ConfigCodeCompletion::~ConfigCodeCompletion()
{
}

void ConfigCodeCompletion::addPage(TQTabWidget *tab, CompletionPage page, const TQString &title, const TQString &dirname)
{
	m_page[page] = new TQWidget(tab);

	m_listview[page] = new TDEListView( m_page[page] );
	m_listview[page]->addColumn(i18n("Complete Files"));
	m_listview[page]->addColumn(i18n("Local File"));
	m_listview[page]->setFullWidth(true);

	TQGridLayout *grid = new TQGridLayout(m_page[page], 1,1, 10,10);
	grid->addWidget(m_listview[page],0,0);

	// add Tab
	tab->addTab(m_page[page],title);

	// remember directory name
	m_dirname << dirname;

	connect(m_listview[page], TQ_SIGNAL(clicked( TQListViewItem *)), this, TQ_SLOT(slotListviewClicked(TQListViewItem *)));
}

//////////////////// read/write configuration ////////////////////

void ConfigCodeCompletion::readConfig(void)
{
   // read selected and deselected filenames with wordlists
   m_wordlist[TexPage] = KileConfig::completeTex();
   m_wordlist[DictionaryPage]  = KileConfig::completeDict();
   m_wordlist[AbbreviationPage]  = KileConfig::completeAbbrev();

   // set checkbox status
   cb_usecomplete->setChecked( KileConfig::completeEnabled() );
   cb_setcursor->setChecked( KileConfig::completeCursor() );
   cb_setbullets->setChecked( KileConfig::completeBullets() );
   cb_closeenv->setChecked( KileConfig::completeCloseEnv() );
	cb_showabbrevview->setChecked( KileConfig::completeShowAbbrev() );
	cb_citeoutofbraces->setChecked( KileConfig::completeCitationMove() );

	// set checkboxes and thresholds for autocompletion modes
	if ( kateCompletionPlugin() )
	{
   	cb_autocomplete->setChecked( false );
		cb_autocompletetext->setChecked( false );
		cb_autocompleteabbrev->setChecked( false );
	}
	else
	{
   	cb_autocomplete->setChecked( KileConfig::completeAuto() );
		cb_autocompletetext->setChecked( KileConfig::completeAutoText() );
		cb_autocompleteabbrev->setChecked( KileConfig::completeAutoAbbrev() );
	}
	sp_latexthreshold->setValue( KileConfig::completeAutoThreshold() );
	sp_textthreshold->setValue( KileConfig::completeAutoTextThreshold() );

   // insert filenames into listview
	for ( uint i=TexPage; i<NumPages; ++i )
		setListviewEntries( CompletionPage(i) );
}

void ConfigCodeCompletion::writeConfig(void)
{
   // default: no changes in configuration
   bool changed = false;

   // get listview entries
	for ( uint i=TexPage; i<NumPages; ++i )
		changed |= getListviewEntries( CompletionPage(i) );

   // Konfigurationslisten abspeichern
   KileConfig::setCompleteTex( m_wordlist[TexPage] );
   KileConfig::setCompleteDict( m_wordlist[DictionaryPage] );
   KileConfig::setCompleteAbbrev( m_wordlist[AbbreviationPage] );

   // save checkbox status
   KileConfig::setCompleteEnabled(cb_usecomplete->isChecked());
   KileConfig::setCompleteCursor(cb_setcursor->isChecked());
   KileConfig::setCompleteBullets(cb_setbullets->isChecked());
   KileConfig::setCompleteCloseEnv(cb_closeenv->isChecked());
	KileConfig::setCompleteShowAbbrev( cb_showabbrevview->isChecked() );
	KileConfig::setCompleteCitationMove( cb_citeoutofbraces->isChecked() );

	// read autocompletion settings
	bool autoModeLatex = cb_autocomplete->isChecked();
	bool autoModeText = cb_autocompletetext->isChecked();
	bool autoModeAbbrev = cb_autocompleteabbrev->isChecked();
	if ( kateCompletionPlugin() )
	{
		if ( autoModeLatex || autoModeText  || autoModeAbbrev)
		{
			TQString msg = i18n("You enabled the KTextEditor-Plugin for word completion, "
			                   "but this conflicts with the auto completion modes of Kile. "
			                   "As only one of these completion modes can be used, the "
			                   "autocompletion modes of Kile will be disabled.");
			KMessageBox::information( 0L,"<center>" + msg + "</center>",i18n("Autocomplete warning") );

			// disable Kile autocomplete modes
			autoModeLatex = false;
			autoModeText = false;
			autoModeAbbrev = false;
		}
	}

	// save settings for Kile autocompletion modes
	KileConfig::setCompleteAuto( autoModeLatex );
	KileConfig::setCompleteAutoText( autoModeText );
	KileConfig::setCompleteAutoAbbrev( autoModeAbbrev );
	KileConfig::setCompleteAutoThreshold( sp_latexthreshold->value() );
	KileConfig::setCompleteAutoTextThreshold( sp_textthreshold->value() );

   // save changed wordlists?
   KileConfig::setCompleteChangedLists(changed);
}

// read kate plugin configuration
bool ConfigCodeCompletion::kateCompletionPlugin()
{
	m_config->setGroup("Kate Document Defaults");
	return m_config->readBoolEntry("KTextEditor Plugin tdetexteditor_docwordcompletion",false);
}

//////////////////// listview ////////////////////

// ListView fr den Konfigurationsdialog einstellen

void ConfigCodeCompletion::setListviewEntries(CompletionPage page)
{
	TQString listname = m_dirname[page];
	TQString localdir = m_localCwlDir + listname + '/';
	TQString globaldir = m_globalCwlDir + listname + '/';

	// Daten aus der Konfigurationsliste in das ListView-Widget eintragen
	m_listview[page]->setUpdatesEnabled(false);
	m_listview[page]->clear();
	TQStringList::ConstIterator it;
	for ( it=m_wordlist[page].begin(); it!=m_wordlist[page].end(); ++it )
	{
		TQString basename = (*it).right( (*it).length()-2 );
		bool localExists = TQFileInfo(localdir+basename+".cwl").exists();
		
		TQCheckListItem *item = new TQCheckListItem(m_listview[page],basename,TQCheckListItem::CheckBox);
		if ( localExists )
		{
			item->setOn( (*it).at(0) == '1' ? true : false );
			item->setText(1,"+");
		}
		else if ( TQFileInfo(globaldir+basename+".cwl").exists() )
		{	
			item->setOn( (*it).at(0) == '1' ? true : false );
		}
		else
		{
			item->setOn(false);
			item->setText(1,i18n("File not found"));
		}
		m_listview[page]->insertItem(item);
	}
	
	updateColumnWidth(m_listview[page]);
	m_listview[page]->setUpdatesEnabled(true);
}

void ConfigCodeCompletion::updateColumnWidth(TDEListView *listview)
{
	listview->setColumnWidth(0,listview->columnWidth(0)+60);
}

bool ConfigCodeCompletion::getListviewEntries(CompletionPage page)
{
   bool changed = false;

   // count number of entries
   uint n = m_listview[page]->childCount();

    // there are changes if this number has changed
   if ( n != m_wordlist[page].count() )
      changed = true;

   // clear all stringlist with files, if there are no entries
   if ( n == 0 ) {
      m_wordlist[page].clear();
      return changed;
   }

   // now check all entries if they have changed
   TQStringList newfiles;
   int index = 0;
   TQCheckListItem *item = (TQCheckListItem *)m_listview[page]->firstChild();
   while ( item ) {
      TQString s = ( item->isOn() ) ? "1-" : "0-";
      s += item->text(0);
      newfiles.append(s);

      // check for a change
      if ( m_wordlist[page][index] != s )
         changed = true;

      // go on
      item = (TQCheckListItem *)item->nextSibling();
      ++index;
   }

   // only update if there are changes
   if ( changed )
      m_wordlist[page] = newfiles;

   return changed;
}

bool ConfigCodeCompletion::isListviewEntry(TDEListView *listview, const TQString &filename)
{
	TQCheckListItem *item = (TQCheckListItem *)listview->firstChild();
	while ( item ) 
	{
		if ( item->text() == filename )
			return true;
		item = (TQCheckListItem *)item->nextSibling();
	}
	return false;
}

//////////////////// tabpages parameter ////////////////////

TDEListView *ConfigCodeCompletion::getListview(TQWidget *page)
{
	for ( uint i=TexPage; i<NumPages; ++i )
	{
		if ( page == m_page[i] )
			return m_listview[i];
	}
	return 0;
}

TQString ConfigCodeCompletion::getListname(TQWidget *page)
{
	for ( uint i=TexPage; i<NumPages; ++i )
	{
		if ( page == m_page[i] )
			return m_dirname[i];
	}
	return TQString();
}

//////////////////// shwo tabpages ////////////////////

void ConfigCodeCompletion::showPage(TQWidget *page)
{
	TDEListView *listview = getListview(page);
	if ( listview ) 
		remove->setEnabled( listview->selectedItems().count() > 0 );
}

//////////////////// add/remove new wordlists ////////////////////

// find local and global resource directories

void ConfigCodeCompletion::getCwlDirs()
{
	m_localCwlDir = locateLocal("appdata","complete/");
	m_globalCwlDir = TQString();

	TQStringList dirs = TDEGlobal::dirs()->findDirs("appdata","complete/");
	for ( TQStringList::ConstIterator it=dirs.begin(); it!=dirs.end(); ++it )
	{
		if ( (*it) != m_localCwlDir )
		{
			m_globalCwlDir = (*it);
			break;
		}
	}
}

// find local and global cwl files: global files are not added,
// if there is already a local file with this name. We fill a map
// with filename as key and filepath as value. Additionally all 
// filenames are added to a stringlist.

void ConfigCodeCompletion::getCwlFiles(TQMap<TQString,TQString> &map, TQStringList &list, const TQString &dir)
{
	TQStringList files = TQDir(dir,"*.cwl").entryList();
	for ( TQStringList::ConstIterator it=files.begin(); it!=files.end(); ++it )
	{
		TQString filename = TQFileInfo(*it).fileName();
		if ( ! map.contains(filename) )
		{
			map[filename] = dir + '/' + (*it);
			list << filename;
		}
	}
}

void ConfigCodeCompletion::addClicked()
{
	// determine current subdirectory for current tab page	
	TQString listname = getListname(tab->currentPage());


	// get a sorted list of all cwl files from both directories
	TQMap<TQString,TQString> filemap;
	TQStringList filelist;
	getCwlFiles(filemap,filelist,m_localCwlDir+listname);
	getCwlFiles(filemap,filelist,m_globalCwlDir+listname);
	filelist.sort();

	// dialog to add cwl files
	KileListSelectorMultiple *dlg  = new KileListSelectorMultiple(filelist,i18n("Complete Files"),i18n("Select Files"), this);
	if ( dlg->exec() ) 
	{
		if ( dlg->currentItem() >= 0 ) 
		{
			TDEListView *listview = getListview(tab->currentPage());     // get current page
			TQStringList filenames = dlg->selected();                   // get selected files
			for ( TQStringList::ConstIterator it=filenames.begin(); it!=filenames.end(); ++it )
			{
				TQString filename = *it;
				// could we accept the wordlist?
				TQFileInfo fi( filemap[filename] );
				if ( !filename.isEmpty() && fi.exists() && fi.isReadable() )
				{
					TQString basename = filename.left(filename.length()-4);

					// check if this entry already exists
					if ( isListviewEntry(listview,basename) )
					{
						m_logwidget->printMsg(KileTool::Info,i18n("Wordlist '%1' is already used.").arg(basename),i18n("Complete"));
						continue;
					}

					// add new entry
					TQCheckListItem *item = new TQCheckListItem(listview,basename,TQCheckListItem::CheckBox);
					item->setOn(true);
					item->setSelected(true);
					if ( filemap[filename].left(m_localCwlDir.length()) == m_localCwlDir )
						item->setText(1,"+");

					listview->insertItem(item);
				}
			}
			updateColumnWidth(listview);
		}
	}
	delete dlg;

}

// delete a selected entry

void ConfigCodeCompletion::removeClicked()
{
   TQWidget *page = tab->currentPage();
   TDEListView *list = getListview(page);                              // determine page
   TQCheckListItem *item = (TQCheckListItem *)list->selectedItem();    // determine entry

	if ( item ) 
	{
		list->takeItem(item);
		delete item;
		// Button enabled/disabled?
		showPage(page);
	}
}

void ConfigCodeCompletion::slotListviewClicked(TQListViewItem *)
{
	TDEListView *listview = getListview(tab->currentPage());     // get current page
	remove->setEnabled( listview->selectedItems().count() > 0 );
}

#include "configcodecompletion.moc"
