/*
 * Copyright (C) 2004 Robert Hogan <robert at roberthogan dot net>
 */

#include "klamav.h"
#include "freshklam.h"
#include "sigtool.h"
#include "klamscan.h"
#include "kuarantine.h"
#include "welcome.h"
#include "dbviewer.h"
#include "activityviewer.h"
#include "version.h"
#include "firstrunwizard.h"
#include "collectiondb.h"
#include "configdialog.h"
#include "tabwidget.h"
#include "klamavconfig.h"

#include <tdelocale.h>


#include <tdeaction.h>
#include <tqpushbutton.h>
#include <tqtooltip.h>
#include <tqlayout.h>
#include <ksystemtray.h>
#include <tdemenubar.h>
#include <tdepopupmenu.h>
#include <kdebug.h>
#include <tdemessagebox.h>
#include <kurl.h>
#include <kiconloader.h>

#include <tqdir.h>
#include <tqfile.h>

Klamav *tdemain = 0L;

using namespace KlamAV;

Klamav::Klamav()
    : TDEMainWindow( 0, "KlamAV " )
{

	downloadDBForWizard = false;

	tdemain = this;
	TQVBoxLayout *top = new TQVBoxLayout(this,3,0);

	// We don't want these tabs closed
	TQStringList fixedTabs;
	fixedTabs << i18n("Scan") << i18n("Update");

	tab = new TabWidget(this, "KlamAV", fixedTabs);
	connect( tab, SIGNAL(tabClosed(TQString)), this, SLOT(tabClosed(TQString)) );

  	_tray = new KSystemTray(tdemain, "klamav tray");
 _tray->setPixmap( KSystemTray::loadIcon("klamav") );
	connect(_tray,SIGNAL(quitSelected()),SLOT(shuttingDown()));

	TDEPopupMenu *conf_menu = _tray->contextMenu();

  EnableFreshklam = new TDEAction(i18n("&Enable Auto-Updates"), "system-software-update", 0,this, SLOT(contextEnableFK()),actionCollection(),"fk_enable");
  DisableFreshklam = new TDEAction(i18n("&Disable Auto-Updates"), 0, 0, this, SLOT(contextDisableFK()),actionCollection(),"fk_disable");

	EnableFreshklam->plug(conf_menu);
	DisableFreshklam->plug(conf_menu);

	TQToolTip::add( _tray, i18n( "KlamAV - Virus Protection for TDE" ) );
	_tray->show();

	DisableFreshklam->setEnabled(FALSE);
	EnableFreshklam->setEnabled(TRUE);

        config = TDEGlobal::config();
        config->setGroup("Freshklam");
        TQStringList DatabasePaths = config->readListEntry("lastDownloadPaths");

        config->setGroup("Kuarantine");
        TQStringList QuarantinePaths = config->readListEntry("KuarantineLocations");

	if ((DatabasePaths.isEmpty()) && (QuarantinePaths.isEmpty())){

            firstRunWizard();
        }

	// Menus
	TDEPopupMenu *scanner_menu = new TDEPopupMenu(this);
	scanner_menu->insertItem( SmallIcon("document-open"), i18n("Scan &File..."), this, SLOT(slotScanFile()), CTRL+Key_O );
	scanner_menu->insertItem( SmallIcon("folder_open"), i18n("Scan &Directory..."), this, SLOT(slotScanDir()) );
	scanner_menu->insertSeparator();
	scanner_menu->insertItem( SmallIcon("xclock"), i18n("&Schedule scan..."), this, SLOT(slotScheduleScan()) );
	scanner_menu->insertItem( SmallIcon("configure"), i18n("&Options..."), this, SLOT(slotOptions()) );

	tabs_menu = new TDEPopupMenu(this);
	tabs_menu->setCheckable(true);
	showWelcomeTab = tabs_menu->insertItem( SmallIcon("klamav"), i18n("Show &Welcome tab"), this, SLOT(slotToggleWelcome()) );
	showQuarantineTab = tabs_menu->insertItem( SmallIcon("encrypted"), i18n("Show &Quarantine tab"), this, SLOT(slotToggleQuarantine()) );
	showDBViewerTab = tabs_menu->insertItem( SmallIcon("system-search"), i18n("Show &Virus Browser tab"), this, SLOT(slotToggleDBViewer()) );
	showEventsTab = tabs_menu->insertItem( SmallIcon("toggle_log"), i18n("Show &Events tab"), this, SLOT(slotToggleEvents()) );


	// Menu bar
	KMenuBar *menubar = this->menuBar();
	menubar->insertItem( i18n("&Scanner"), scanner_menu );
	menubar->insertItem( i18n("&Tabs"), tabs_menu );
	menubar->insertItem( i18n("&Help"), helpMenu(NULL,false) );

	top->setMenuBar(menubar);

	activityviewer = new Activityviewer(this, "Events");

	aboutklamav = new Aboutklamav(this, "Welcome");
	updateTabState(0, true); // Welcome tab

	klamscan = new Klamscan(this);
	tab->addTab(klamscan, i18n("Scan"));

	freshklam = new Freshklam(this);
	tab->addTab(freshklam, i18n("Update"));

	connect(freshklam->search_button,SIGNAL(clicked()),SLOT(contextUpdateFK()));
	connect(freshklam->cancel_button,SIGNAL(clicked()),SLOT(contextDisableFK()));
	
	
	
	kuarantine = new Kuarantine(this, "Quarantine");
	updateTabState(1, true); // Quarantine tab

	klamdb = new KlamDB(this, "DBViewer");
	updateTabState(2, true); // Virus Browser tab

	updateTabState(3, true); // Events tab


	top->addWidget(tab);
	
	
	connect(tab,SIGNAL(currentChanged ( TQWidget * ) ),klamdb,SLOT(shouldIShow(TQWidget *)));

	KStdAction::quit(this, SLOT(shuttingDown()), actionCollection());
  
  	if (TDEApplication::kApplication()->isRestored()){
		hide();
	}else{
		show();
   	}

	
	if ((firstDownload) || (downloadDBForWizard)){
		kdDebug() << "firstdownload" << firstDownload << endl;
		kdDebug() << "downloadDBForWizard" << downloadDBForWizard << endl;
		tab->setCurrentPage(2);
		freshklam->slotSearch();
	}
	//klamdb->shouldIShow();

	CollectionDB::instance()->insertEvent("Launch","KlamAV Launched");

    setCaption(TQString("KlamAV %1 (Using ClamAV %2)").arg(KLAMAV_VERSION).arg(KlamavConfig::clamAVVersion()));
}
	
Klamav::~Klamav()
{
    KlamavConfig::writeConfig();
	kapp->quit();
}

void  Klamav::shuttingDown(){

	this->topLevelWidget()->hide();

}

bool Klamav::queryClose() {
	if ( freshklam->isFreshklamAlive() || klamscan->scanGoingOn() ){
        KMessageBox::information (this,i18n("<p>KlamAV will stay open in the system tray. <br><br>"
        " <b>Remember</b> - you can't quit KlamAV while <br> "
        " scanning or auto-updating!</p>"),"KlamAV","dontshow");
		hide();
		return false;

	}
	return true;
}


void Klamav::clamdStopped() {
/*	if (freshklam->isFreshklamAlive())
		_tray->setPixmap(KSystemTray::loadIcon("klamavbwdl"));
	else*/
	_tray->setPixmap(KSystemTray::loadIcon("klamav_on_acc_disabled"));
}

void Klamav::updateTabState( int tabId, bool init ) {
	if( config->group() != "Tabs" )
		config->setGroup("Tabs");

	TQString optionName, tabName;
	TQWidget* tabWidget;
	int itemId;

	switch(tabId) {
		case 0: // Welcome tab
			optionName="ShowWelcomeTab";
			tabName=i18n("Welcome");
			tabWidget=aboutklamav;
			itemId=showWelcomeTab;
			break;

		case 1: // Quarantine tab
			optionName="ShowQuarantineTab";
			tabName=i18n("Quarantine");
			tabWidget=kuarantine;
			itemId=showQuarantineTab;
			break;

		case 2: // DBViewer tab
			optionName="ShowDBViewerTab";
			tabName=i18n("Virus Browser");
			tabWidget=klamdb;
			itemId=showDBViewerTab;
			break;

		case 3: // Events tab
			optionName="ShowEventsTab";
			tabName=i18n("Events");
			tabWidget=activityviewer;
			itemId=showEventsTab;
			break;
	}

	if( config->readBoolEntry(optionName, true) ) {
		tab->insertTab(tabWidget, tabName.ascii());
		tabWidget->show();

		if(!init) tab->setCurrentPage( tab->indexOf(tabWidget) );
	} else {
		if( tab->currentPageIndex() == tabId )
			tab->setCurrentPage(0);

		if( tab->indexOf( tabWidget ) != -1 ) {
			tab->removePage( tabWidget );
		}
		tabWidget->hide();
	}

	tabs_menu->setItemChecked(itemId, config->readBoolEntry(optionName, true) );
}

void Klamav::tabClosed(TQString name) {
	int uncheck;
	TQString optionName;

	if ( name == "Welcome" ) {
		uncheck=showWelcomeTab;
		optionName="ShowWelcomeTab";
	} else if ( name == "Quarantine" ) {
		uncheck=showQuarantineTab;
		optionName="ShowQuarantineTab";
	} else if ( name == "DBViewer" ) {
		uncheck=showDBViewerTab;
		optionName="ShowDBViewerTab";
	} else if ( name == "Events" ) {
		uncheck=showEventsTab;
		optionName="ShowEventsTab";
	}

  tabs_menu->setItemChecked(uncheck, false);
  config->setGroup("Tabs");
	config->writeEntry(optionName, false);
}

// Menu slots
void Klamav::slotScanFile() {
	klamscan->slotScanFile();
}

void Klamav::slotScanDir() {
	klamscan->slotScanDir();
}

void Klamav::slotScheduleScan() {
	klamscan->slotSchedule();
}
void Klamav::slotOptions() {
	slotConfigKlamav("Scanning Backend");
}

void Klamav::slotToggleWelcome() {
  if( config->group() != "Tabs" )
    config->setGroup("Tabs");

	bool newState = ! config->readBoolEntry("ShowWelcomeTab", true);
	config->writeEntry("ShowWelcomeTab", newState);
	config->sync();

	updateTabState(0, false);
}
void Klamav::slotToggleQuarantine() {
  if( config->group() != "Tabs" )
    config->setGroup("Tabs");

	bool newState = ! config->readBoolEntry("ShowQuarantineTab", true);
	config->writeEntry("ShowQuarantineTab", newState);
	config->sync();

	updateTabState(1, false);
}
void Klamav::slotToggleDBViewer() {
  if( config->group() != "Tabs" )
    config->setGroup("Tabs");

	bool newState = ! config->readBoolEntry("ShowDBViewerTab", true);
	config->writeEntry("ShowDBViewerTab", newState);
	config->sync();

	updateTabState(2, false);
}
void Klamav::slotToggleEvents() {
  if( config->group() != "Tabs" )
    config->setGroup("Tabs");

	bool newState = ! config->readBoolEntry("ShowEventsTab", true);
	config->writeEntry("ShowEventsTab", newState);
	config->sync();

	updateTabState(3, false);
}


void Klamav::contextUpdateFK() {

    freshklam->slotSearch();
}

void Klamav::contextEnableFK() {

    freshklam->enableAutoUpdates();
}

void Klamav::contextDisableFK() {
	
	freshklam->slotCancel();
// 	DisableFreshklam->setEnabled(FALSE);
// 	EnableFreshklam->setEnabled(TRUE);

}
void Klamav::showVirusBrowser() {
	tab->setCurrentPage(5);
}

void Klamav::firstRunWizard() {

        FirstRunWizard wizard;
        wizard.setCaption( i18n( "First-Run Wizard" ));
        
        TQString homepath = getenv("HOME");
        TQString defaultdb = homepath + "/.klamav/database";
        TQString defaultquar = homepath + "/.klamav/quarantine";
    
	// Execute wizard
	wizard.exec();

        // These are run after the wizard is finished
        TDEConfig* config = TDEGlobal::config();

        config->setGroup("Freshklam");
            
        TQString wizardDBPath = wizard.databasePath().path(-1);
        TQString wizardTQRPath = wizard.quarantinePath().path(-1);

        //Configure Database Path
        TQStringList lastDownloadPaths;
        if ((wizardDBPath != "") && (TQDir::cleanDirPath(wizardDBPath) != defaultdb)){
            lastDownloadPaths.prepend( TQString("%1").arg(wizardDBPath));
            checkDir(wizardDBPath);
        }else{
            lastDownloadPaths.prepend( TQString("%1").arg(wizard.defaultDatabasePath()));
            createDefaultKlamAVDir("database");
        }
        config->writeEntry("lastDownloadPaths", lastDownloadPaths);
        config->sync();

        //Configure Quarantine Path
        TQStringList lastQuarLocations;
        
        config->setGroup("Kuarantine");
            
        if ((wizardTQRPath != "") && (TQDir::cleanDirPath(wizardTQRPath) != defaultquar)){
            lastQuarLocations.prepend( TQString("%1").arg(wizardTQRPath));
            checkDir(wizardTQRPath);
        }else{
            lastQuarLocations.prepend( TQString("%1").arg(wizard.defaultQuarantinePath()));
            createDefaultKlamAVDir("quarantine");
        }
        config->writeEntry("KuarantineLocations", lastQuarLocations);
        config->sync();

        if (wizard.downloadDatabase()){
            kdDebug() << "downloading database" << endl;
            downloadDBForWizard = true;
        }


}

void  Klamav::createDefaultKlamAVDir(TQString type){

    TQString path = getenv("HOME");
    bool ok = true;
    // directory exist?
    path += "/.klamav";
    TQDir klamavdir(path);
    if (!klamavdir.exists() && !klamavdir.mkdir(path))
        ok = false;

    path += "/" + type;
    if (ok)
    {
        TQDir klamavqdir(path);
        if (!klamavqdir.exists() && !klamavqdir.mkdir(path))
            ok = false;
        else
            chmod(path.ascii(),0700);
    }

}

void  Klamav::checkDir(TQString path){

    TQDir klamavdir(path);
    TQFile f1( path );
    if ((!klamavdir.exists()) || (f1.open( IO_ReadWrite )))
        KMessageBox::information(this, i18n("Either the directory %1 does not exist or you are not able to write to it. Either way, you will have to change it as it cannot be used. Sorry!").arg(path));
    f1.close(); 

}

void Klamav::slotConfigKlamav( const TQCString& page )
{

    KlamavConfigDialog* dialog = (KlamavConfigDialog*) TDEConfigDialog::exists( "settings" );

    if( !dialog )
    {
        //TDEConfigDialog didn't find an instance of this dialog, so lets create it :
        dialog = new KlamavConfigDialog( this, "settings", KlamavConfig::self() );
        //connect( dialog, SIGNAL(settingsChanged()), SLOT(applySettings()) );
    }

    //FIXME it seems that if the dialog is on a different desktop it gets lost
    //      what do to? detect and move it?

    dialog->show();
    dialog->raise();
    dialog->setActiveWindow();

    //so that if the engine page is needed to be shown it works

    if ( !page.isNull() ) dialog->showPage( page );
}


#include "klamav.moc"