/***************************************************************************
 *   Copyright (C) 2005 by Nicolas Ternisien                               *
 *   nicolas.ternisien@gmail.com                                           *
 *                                                                         *
 *   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 Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

//TQt includes
#include <tqclipboard.h>

//KDE includes
#include <tdepopupmenu.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <tdeapplication.h>

#include <tdeio/netaccess.h>
#include <kfilterdev.h>

#include <tdefiledialog.h>
#include <tdemessagebox.h>

//Project includes
#include "logManager.h"

#include "reader.h"
#include "view.h"

#include "ksystemlog.h"
#include "readerFactory.h"

#include "loadingDialog.h"

LogManager::LogManager(KSystemLog* m, LoadingDialog* progress) :
	main(m),
	loadingDialog(progress),
	reader(NULL),
	view(NULL),
	
	groupBy(NO_GROUP_BY),
	groupByColumn(-1),
	columns(NULL),
	
	sortOrder(TQt::Ascending),
	sortColumn(0),

	tooltipEnabled(true),
	newLinesDisplayed(true),
	parsingPaused(false),
	
	logFiles(),

	current(false),
	newLinesSinceLastSelection(0),
	lastUpdate(TQTime::currentTime()),

	logMode(Globals::noMode) {
	
	//TODO Create the view from a factory for example
	view=new View(main);
	
	view->setLogManager(this);
	connect(view, TQ_SIGNAL(changeStatusbar(const TQString&)), this, TQ_SLOT(slotChangeStatusbar(const TQString&)));
	connect(view, TQ_SIGNAL(changeCaption(const TQString&)), this, TQ_SLOT(slotChangeCaption(const TQString&)));
	
	connect(view->getLogList(), TQ_SIGNAL(doubleClicked(TQListViewItem *, const TQPoint&, int)), this, TQ_SLOT(slotDetails(TQListViewItem *, const TQPoint&, int)));
	connect(view->getLogList(), TQ_SIGNAL(selectionChanged()), this, TQ_SLOT(slotSelectionChanged()));
	connect(view->getLogList(), TQ_SIGNAL(rightButtonPressed(TQListViewItem*, const TQPoint&, int)), this, TQ_SLOT(slotOpenContextualMenu(TQListViewItem*, const TQPoint&, int)));

}

LogManager::~LogManager() {
	//TODO Delete all used object (view and reader)
	delete view;
	
	delete reader;
}

View* LogManager::getView() {
	return(view);
}

void LogManager::saveConfig() {
	view->saveConfig();
}

void LogManager::slotSelectionChanged() {
	emit selectionChanged();
}

Reader* LogManager::getReader() {
	return(reader);
}

int LogManager::getIndex() {
	return(main->getIndex(this));
}

void LogManager::initView() {
	if (columns!=NULL)
		delete columns;
		
	//Construct columns list
	columns=new LogViewColumns();
	
	//Initialize columns list from Reader class
	reader->initColumns(columns);
	
	//If groupBy feature is activated, we must draw the tree lines in the view 
	if (groupBy!=NO_GROUP_BY)
		view->getLogList()->setRootIsDecorated(true);
	else
		view->getLogList()->setRootIsDecorated(false);
	
	
	view->setColumns(columns);
}

void LogManager::reload() {

	loadingDialog->show();
	loadingDialog->progressBar()->setProgress(0);
	loadingDialog->setLoadingLog(logMode->name);
	
	emit changeStatusbar(i18n("Loading log..."));
	
	kdDebug() << "Reloading " << logMode->name << "..." << endl;
	
	//Change part of the main interface
	emit changeTitle(view, logMode->pixmap, logMode->name);
	emit changeCaption(logMode->name);
	
	//Empty the current list, to better fill it
	clearView();
	
	//Init the Log View
	initView();
		
	//Read the log files
	reader->readLog();
	
	emit changeStatusbar(i18n("Log successfully loaded."));
	
	//Emit this signal only when the Log List has been totally reloaded
	emit reloaded();
	
	loadingDialog->progressBar()->setProgress(100);
	
	loadingDialog->hide();

}

void LogManager::openingProgress(int percent) {
	loadingDialog->progressBar()->setProgress(percent);
	
	//Move this signal to Loading Dialog class
	emit GUIUpdated();
	//((KSystemLog*)( view->parent()))->kapp->processEvents();
}


void LogManager::toggleFilterBar() {
	view->toggleFilterBar();
}

void LogManager::slotDetails(TQListViewItem *, const TQPoint &, int) {
	emit detailsCalled();
}

void LogManager::slotChangeCaption(const TQString& message) {
	emit changeCaption(message);
}

void LogManager::slotChangeStatusbar(const TQString& message) {
	emit changeStatusbar(message);
}

LogViewColumns* LogManager::getColumns() {
	return(columns);
}



void LogManager::clearView() {
	view->clearLogList();
}

void LogManager::setLogFiles(LogFiles* files) {
	//We first clear the previous list
	//TODO Also delete pointers in this list before empty it
	logFiles.clear();
	
	LogFiles::Iterator it;
	for(it=files->begin(); it!=files->end(); ++it) {
		logFiles.append(*it);
	}
	
	//The list is (normally) no longer used after that.
	delete files;
}


LogFiles& LogManager::getLogFiles() {
	return(logFiles);
}

LogMode* LogManager::getLogMode() {
	return(logMode);
}

TQTime& LogManager::getLastUpdate() {
	return(lastUpdate);
}


void LogManager::slotLogUpdated(int lineCount) {
	kdDebug() << "Log is updated (from LogManager) : " << lineCount  << " current=" << isCurrent() << endl;
	
	if (lineCount==0)
		return;
	
	newLinesSinceLastSelection+=lineCount;
	
	lastUpdate=TQTime::currentTime();
	
	//Emit this signal even if the current LogManager is selected
	emit logUpdated(newLinesSinceLastSelection);
	
	//If the user is in this log manager, he does have to see that some new log lines have appeared
	if (isCurrent()==false) {
		TQString title(i18n("%1 (%2)").arg(logMode->name).arg(newLinesSinceLastSelection));
		
		//This signal is emitted only if the selected tab is not this one
		emit changeTitle(view, title);
	}
	
}

void LogManager::slotExpandAll() {
	//If this LogManager is the currently displayed one
	if (isCurrent()) {
		TQListViewItem* item=view->getLogList()->firstChild();
		while (item!=NULL) {
			item->setOpen(true);
			
			item=item->nextSibling();
		}
	}
	
}

void LogManager::slotCollapseAll() {
	//If this LogManager is the currently displayed one
	if (isCurrent()) {
		TQListViewItem* item=view->getLogList()->firstChild();
		while (item!=NULL) {
			item->setOpen(false);
			
			item=item->nextSibling();
		}
	}	
}

void LogManager::slotFileSave() {
	//If this LogManager is the currently displayed one
	if (isCurrent()) {
		TQListViewItemIterator it(view->getLogList(), TQListViewItemIterator::Selected);
		
		//No item selected
		if (it.current()==NULL) {
			emit changeStatusbar(i18n("No items selected. Please select items to be able to save them."));
			return;
		}
		
	
		TQString filename = KFileDialog::getSaveFileName(TQString(), TQString(), main);
		if (!filename.isEmpty()) {
			
			TQIODevice* ioDev = KFilterDev::deviceForFile( filename );
			if (ioDev->open(IO_WriteOnly)) {
				TQTextStream stream(ioDev);
				
				int nbCopied=0;
				
				TQListViewItem* qtItem=it.current();
				while (qtItem!=NULL) {
					LogListItem* item=static_cast<LogListItem*> (qtItem);
					
					//Copy the item content to the stream
					stream << item->exportToText();
					
					//Retrieve the next item
					it++;
					qtItem=it.current();
					nbCopied++;
					
				}
				
				ioDev->close();
				
				emit changeStatusbar(i18n("1 log line saved to '%1'.", "%n log lines saved to '%1'.", nbCopied).arg(filename));
			}
			else {
				TQString message(i18n("Unable to save file '%1': Permission Denied.").arg(filename));
				KMessageBox::error(main, message, i18n("Unable to save file."), KMessageBox::Notify);
			}
			
			delete ioDev;
		}
	}
}

void LogManager::reinitialize() {
	//Reinitialize groupBy
	groupBy=NO_GROUP_BY;
	groupByColumn=-1;
	
	//TODO Reinitialize sorting, order, column order...
}
	
void LogManager::initialize(LogMode* mode) {
	//If this LogManager has already been initialized
	//with another LogMode, then it needs to be reinitialized
	if (this->logMode!=Globals::noMode)
		reinitialize();

	//Use now the new mode
	this->logMode=mode;

	//Find the reader instance used for this new mode
	reader=ReaderFactory::createReader(mode);
	connect(reader, TQ_SIGNAL(statusbarChanged(const TQString&)), this, TQ_SLOT(slotChangeStatusbar(const TQString&)));
	connect(reader, TQ_SIGNAL(logUpdated(int)), this, TQ_SLOT(slotLogUpdated(int)));
	connect(reader, TQ_SIGNAL(openingProgressed(int)), this, TQ_SLOT(openingProgress(int)));
	
	connect(reader, TQ_SIGNAL(readingBegin()), this, TQ_SLOT(readingBegun()));
	connect(reader, TQ_SIGNAL(readingFile(int)), this, TQ_SLOT(readingFile(int)));
	connect(reader, TQ_SIGNAL(readingEnd()), this, TQ_SLOT(readingEnded()));

	
	//Inform this reader that this LogManager is now its parent
	reader->setLogManager(this);
	
	//Find the log files used for this kind of mode, and set them to our log manager
	setLogFiles(ReaderFactory::createLogFiles(mode));

}
	
void LogManager::readingBegun() {
	//Inform the Loading Dialog on how many Files we have to load
	loadingDialog->setFileCount(logFiles.count());
}

void LogManager::readingFile(int currentFile) {
	//Inform the Loading Dialog that there is a new file loading
	loadingDialog->setCurrentFile(currentFile);
	
	//Update the Progress Dialog label
	loadingDialog->show();
	loadingDialog->progressBar()->setProgress(0);
	loadingDialog->setLoadingLog(logMode->name);
	
}

void LogManager::readingEnded() {
	//Reposition the count total to its default value
	loadingDialog->setFileCount(0);
}


void LogManager::setParsingPaused(bool paused) {
	parsingPaused=paused;
	
	//If we resume the parsing, then we reparse the files to know if new lines has append
	if (parsingPaused==false) {
		if (reader!=NULL) {
			reader->relaunchLogChanged();
		}
	}
	
}

bool LogManager::isParsingPaused() {
	return(parsingPaused);
}


void LogManager::setTooltipEnabled(bool enabled) {
	tooltipEnabled=enabled;
}

bool LogManager::isTooltipEnabled() {
	return(tooltipEnabled);
}

void LogManager::setCurrent(bool cur) {
	current=cur;
	
	//Reinitialize the new line count
	newLinesSinceLastSelection=0;

}

bool LogManager::isCurrent() {
	return(current);
}


void LogManager::setNewLinesDisplayed(bool displayed) {
	newLinesDisplayed=displayed;
}

bool LogManager::isNewLinesDisplayed() {
	return(newLinesDisplayed);
}

void LogManager::setGroupBy(groupByType group, int column) {
	this->groupBy=group;
	this->groupByColumn=column;
}

groupByType LogManager::getGroupBy() {
	return(groupBy);
}

int LogManager::getGroupByColumn() {
	return(groupByColumn);
}


void LogManager::slotSendMail() {
	//If this LogManager is the currently displayed one
	if (isCurrent()) {
	
		TQString body(i18n("Here are my logs:\n"));
		
		body+=i18n("---------------------------------------\n");
		
		TQListViewItemIterator it(view->getLogList(), TQListViewItemIterator::Selected);
		
		int i=0;
		
		TQListViewItem* qtItem=it.current();
		while (qtItem!=NULL) {
			LogListItem* item=static_cast<LogListItem*> (qtItem);
			
			body+= item->exportToText();
			body+='\n';
			
			++it;
			qtItem=it.current();
			++i;
		}
		body+=i18n("---------------------------------------\n");
		
		//Too much lines selected
		if (i>1000) {
			KMessageBox::sorry(main, i18n("You have selected too many lines. Please only select important log lines."), i18n("Too Many Lines Selected"));
			return;
		}
	
		/* Parameters list of this method
		const TQString &   to, 
		const TQString &   cc, 
		const TQString &   bcc, 
		const TQString &   subject, 
		const TQString &   body, 
		const TQString &   messageFile, 
		const TQStringList &   attachURLs, 
		const TQCString &   startup_id
		*/
		kapp->invokeMailer("", "", "", i18n("Log Lines of my problem"), body, "", TQStringList(), kapp->startupId());
	
	}
}

void LogManager::slotCopyToClipboard() {
	//If this LogManager is the currently displayed one
	if (isCurrent()) {
		kdDebug() << "Copy to the clipboard" << endl;
		
		TQListView* listView=view->getLogList();
		
		TQListViewItemIterator it(listView, TQListViewItemIterator::Selected);
		
		int nbCopied=0;
		
		LogListItem* item=static_cast<LogListItem*> (it.current());
		
		TQString text;
		
		while (item!=NULL) {
			
			//Copy the item content to the text string
			text.append(item->exportToText());
			text.append('\n');
			
			//Retrieve the next value
			it++;
			item=static_cast<LogListItem*> (it.current());
			nbCopied++;
			
		}
		
		//Copy text value only if it is not empty
		if (nbCopied==0) {
			emit changeStatusbar(i18n("No items selected. Nothing copied to clipboard."));
		}
		else {
			//Copy both to clipboard and X11-selection
			TQApplication::clipboard()->setText(text, TQClipboard::Clipboard);
			TQApplication::clipboard()->setText(text, TQClipboard::Selection);
			
			emit changeStatusbar(i18n("1 log line copied to clipboard.", "%n log lines copied to clipboard.", nbCopied));
		}
	}
	
}


void LogManager::synchronize(LogLineList* buffer) {
	kdDebug() << "Synchronizing the buffer..." << endl;
	
	//Here to find a bug
	if (buffer==NULL)
		kdDebug() << "ERROR! Buffer NULL" << endl;
	else
		kdDebug() << "Buffer not NULL" << endl;
	
	//Here to find a bug
	if (view==NULL)
		kdDebug() << "ERROR! View NULL" << endl;
	else
		kdDebug() << "View not NULL" << endl;

	//Synchronize the buffer returned by the reader and get the last added line
	LogLine* line=buffer->synchronize(view);
	
	//Here to find a bug
	if (line==NULL)
		kdDebug() << "ERROR! Line NULL" << endl;
	else
		kdDebug() << "Line not NULL" << endl;
	
	//Here to find a bug
	if (view->getLogList()==NULL)
		kdDebug() << "ERROR! TDEListView NULL" << endl;
	else
		kdDebug() << "TDEListView not NULL" << endl;
	
	if (isNewLinesDisplayed()==true) {
		if (line!=NULL) {
			line->ensureItemVisible(view->getLogList());
		}
	}
	
	kdDebug() << "Buffer synchronized." << endl;


}

//TODO Change the method name
void LogManager::slotOpenContextualMenu(TQListViewItem* /*item*/, const TQPoint& pos, int /*other*/) {
	TQPopupMenu menu(main);
	
	main->actionCollection()->action("reload")->plug(&menu);
	main->actionCollection()->action("select_all")->plug(&menu);
	
	menu.insertSeparator();
	
	main->actionCollection()->action("copy")->plug(&menu);
	main->actionCollection()->action("file_save")->plug(&menu);
	
	menu.insertSeparator();
	
	main->actionCollection()->action("enable_tooltip")->plug(&menu);
	main->actionCollection()->action("display_new_line")->plug(&menu);
	main->actionCollection()->action("group_by")->plug(&menu);
	
	menu.insertSeparator();
	
	main->actionCollection()->action("details")->plug(&menu);
	
	menu.exec(pos);
}

#include "logManager.moc"
