/*KSlacky InfoPkg is a package information tool that provide
the list of packages installe on your Slackware system
Copyright (C) 2008  Giovanni Santostefano

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 3 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, see <http://www.gnu.org/licenses/>.*/

#include <QMessageBox>
#include <QFileDialog>
#include <QDir>
#include <QStringList>
#include <QTextStream>
#include <QRegExp>

#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include <string>

#include "mainwindow.h"
#include "upgradewindow.h"
#include "installwindow.h"


using namespace std;

MainWindow::MainWindow(QWidget *parent)
: QWidget(parent)
{
	//set the search index to 0
	searchindex=0;
	
	//setup the ui interface and bind signals to slots
	ui.setupUi(this);
    	connect( ui.pkglist, SIGNAL( itemSelectionChanged() ),
             this, SLOT( PackageListIndexChange() ) );
	connect( ui.aboutbutton, SIGNAL( clicked() ),
             this, SLOT( AboutButtonPressed() ) );
	connect( ui.exitbutton, SIGNAL( clicked() ),
             this, SLOT( ExitButtonPressed() ) );
	//hit enter on the search text lineedit
    	connect( ui.expsearchline, SIGNAL( returnPressed() ),
		 this, SLOT( SearchNextButtonPressed() ) );
    	//search button pressed  
	connect( ui.searchbutton, SIGNAL( clicked() ),
             this, SLOT( SearchButtonPressed() ) );
	//search next button pressed
	connect( ui.searchnextbutton, SIGNAL( clicked() ),
             this, SLOT( SearchNextButtonPressed() ) );
             
	connect( ui.installbutton, SIGNAL( clicked() ),
             this, SLOT( InstallPkgButtonPressed() ) );
	connect( ui.upgradebutton, SIGNAL( clicked() ),
             this, SLOT( UpgradePkgButtonPressed() ) );    
	connect( ui.removebutton, SIGNAL( clicked() ),
             this, SLOT( RemovePkgButtonPressed() ) ); 
	connect( ui.infobutton, SIGNAL( clicked() ),
		 this, SLOT( InfoButtonPressed() ) );                     
	connect( ui.refreshbutton, SIGNAL( clicked() ),
             this, SLOT( RefreshButtonPressed() ) );         
    
	//Set the default pattern string
	pattern="";
	//load the package list
    	PKGLoading();
}

MainWindow::~MainWindow()
{
}

//This method loads the package list
//based on package descriptor files in
// /var/log/packages/*
void MainWindow::PKGLoading()
{
	//open the directory with package descriptor files
	QDir dirman=QDir("/var/log/packages");
	
	//loads in the lcd the package cout
	ui.lcdNumber->display((int)dirman.count());
	
	dirman.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
    	dirman.setSorting(QDir::Name);

    	QFileInfoList list = dirman.entryInfoList();
	
	//clear the list
	ui.pkglist->clear();

	//iterate throug the files and add them on the list
	for(int i=0; i<list.size(); ++i)
	{
		QFileInfo fileinfo = list.at(i);
		ui.pkglist->addItem(fileinfo.fileName());
	}
	
	//set the progress bar bounds
	ui.progressbar->setMaximum(list.size());
	
	//set the first package on the list and show descriptions
	ui.pkglist->setCurrentRow(0);
	ShowFileInfo(0);
}

//Slot for the package list index change
//When a user click on a list entry the 
//program will switch to the selected package
//description
void MainWindow::PackageListIndexChange()
{
	ShowFileInfo(ui.pkglist->currentRow());
}

//Click on exit button will correctly end
//the program
void MainWindow::ExitButtonPressed()
{
	exit(0);
}

//Event generated by the search button pressed
void MainWindow::SearchButtonPressed()
{
	//start by position 0
	searchindex=0;
	
	//store the pattern of search
	pattern=ui.expsearchline->text();
	
	//regex search checked	
	if(ui.radioregexsearch->isChecked())
	{
		//generating the regular expression
		QRegExp rx(ui.expsearchline->text());
		rx.setPatternSyntax(QRegExp::Wildcard);
		rx.setCaseSensitivity(Qt::CaseSensitive);
		
		//search the first pkg name that match the regex
		for(int i=0; i<ui.pkglist->count(); ++i)
		{
			QString s=ui.pkglist->item(i)->text();
			ui.progressbar->setValue(i);
			if(rx.exactMatch(s)) //package found
			{
				//give the focus to the row
				//of the package
				ui.pkglist->setCurrentRow(i);
				//show the package info
				ShowFileInfo(i);
				
				//give back the focus to the search bar text
				//and select the text
				ui.expsearchline->setFocus();
				ui.expsearchline->selectAll();
				return;
			}
			++searchindex;
		}
	}
	else //normal search selected
	{
		//the pattern is a regular expression with * before and after 
		//the expression
		QString pattern="*"+ui.expsearchline->text()+"*";
		
		//generating the regular expression
		QRegExp rx(pattern);
		rx.setPatternSyntax(QRegExp::Wildcard);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		
		//iterate throug the package list and search the first match
		for(int i=0; i<ui.pkglist->count(); ++i)
		{
			QString s=ui.pkglist->item(i)->text();
			ui.progressbar->setValue(i);
			if(rx.exactMatch(s)) //package name match the expression
			{
				//highlight the row and show the info
				//of the package
				ui.pkglist->setCurrentRow(i);
				ShowFileInfo(i);
				
				//give back the focus on the search bar text
				//and highlight the text
				ui.expsearchline->setFocus();
				ui.expsearchline->selectAll();
				return;
			}
			++searchindex;
		}
	}
	
	//package not foud! :(
	ui.pkgdesc->setPlainText("Package not found");
	//give back the focus to the search bar text
	//and highlight the text
	ui.expsearchline->setFocus();
	ui.expsearchline->selectAll();
}

//Search next button pressed
//This slot continue the search from
//the current index.
//--
//This feature will be removed in next
//releases when the search became a filter
//on the list
void MainWindow::SearchNextButtonPressed()
{
	//if the pattern is already set then 
	//use Searchnext
	if(pattern!=ui.expsearchline->text())
	{
		SearchButtonPressed();
		return;
	}

	if(ui.radioregexsearch->isChecked())  //regex selected
	{
		//generating the regular expression
		QRegExp rx(ui.expsearchline->text());
		rx.setPatternSyntax(QRegExp::Wildcard);
		rx.setCaseSensitivity(Qt::CaseSensitive);
		
		//continue to iterate throug the list by the last position
		//searching for regex package matching
		for(int i=++searchindex; i<ui.pkglist->count(); ++i)
		{
			QString s=ui.pkglist->item(i)->text();
			ui.progressbar->setValue(i);
			if(rx.exactMatch(s)) //package match!
			{
				//select the row
				ui.pkglist->setCurrentRow(i);
				//show the file info
				ShowFileInfo(i);
				//give back the focus to the search text box
				//and highlight the text
				ui.expsearchline->setFocus();
				ui.expsearchline->selectAll();
				return;
			}
			++searchindex;
		}
	}
	else //normal search is selected
	{
		//the pattern is a regular expression with * before and after 
		//the expression
		QString pattern="*"+ui.expsearchline->text()+"*";
		
		//generating the regular expression
		QRegExp rx(pattern);
		rx.setPatternSyntax(QRegExp::Wildcard);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		
		//continue to iterate from the searchindex position to find
		//the first occurrency that match the expression
		for(int i=++searchindex; i<ui.pkglist->count(); ++i)
		{
			QString s=ui.pkglist->item(i)->text();
			ui.progressbar->setValue(i);
			if(rx.exactMatch(s)) //package match!
			{
				//select that row
				ui.pkglist->setCurrentRow(i);
				//show the file info
				ShowFileInfo(i);
				//give back the focus to the search line text
				//and highlight the text
				ui.expsearchline->setFocus();
				ui.expsearchline->selectAll();
				return;
			}
			++searchindex;
		}
	}
	
	//package not found
	ui.pkgdesc->setPlainText("Package not found");
}

//Slot when About button is pressed.
//It shows the credits and the licence
void MainWindow::AboutButtonPressed()
{
	QMessageBox msgBox(this);
	msgBox.setWindowTitle("About KSlacky InfoPkg");
	msgBox.setText( "KSlacky InfoPkg (http://kslackyinfopkg.sourceforge.net/) \
\n\nKSlacky InfoPkg is a Slackware information tool\
 that shows info on installed packages\
\nDeveloped by Giovanni Santostefano\
\nhttp://santostefanogiovanni.blogspot.com\n-\n\
Web design and packaging by danix\n\
http://danix.netsons.org\
    \n\nThanks to:\
    \n Slacky community - http://www.slacky.eu\
    \n ksniffer - http://www.ksniffer.org/\
    \n alessiodf - http://www.capponcino.it/alessio\
    \n goldy\
    \n pinotree - (#kde-devel-it user on irc.freenode.org)");
 	msgBox.setStandardButtons(QMessageBox::Close);
 	msgBox.setIconPixmap(QPixmap(":/rc/ksip_icon.png"));
 	msgBox.exec();
 	
 	msgBox.setWindowTitle("KSIP under GPL v3 licence");
 	msgBox.setText("KSlacky InfoPkg is a package information tool that provide\
\nthe list of packages installe on your Slackware system\
\nCopyright (C) 2008  Giovanni Santostefano\
\n\
\nThis program is free software: you can redistribute it and/or modify\
\nit under the terms of the GNU General Public License as published by\
\nthe Free Software Foundation, either version 3 of the License, or\
\n(at your option) any later version.\
\n\
\nThis program is distributed in the hope that it will be useful,\
\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\
\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\
\nGNU General Public License for more details.\
\n\
\nYou should have received a copy of the GNU General Public License\
\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.");
	msgBox.exec();
	
}

//This method loads package description and
//package file list.
void MainWindow::ShowFileInfo(int index)
{
	QString filepath="/var/log/packages/";
	filepath+=ui.pkglist->item(index)->text();
	
	//file opening and reading the content
	QFile file(filepath);
	if(!file.open(QIODevice::ReadOnly))
	{
		ui.pkgdesc->setPlainText("Can't retreive package informations");
		ui.pkgfiles->setPlainText("Can't retreive package file list");
		return;
	}
	
	QString content=QTextStream(&file).readAll();
	
	//split the file description in 2 parts separe by the string FILE LIST
	//the first is the package description, the second is the file list
	QStringList liststrings = content.split("FILE LIST");
	if(liststrings.empty())
	{
		ui.pkgdesc->setPlainText("Can't retreive package informations");
		ui.pkgfiles->setPlainText("Can't retreive package file list");
	}
	else
	{
		//set the package description
		ui.pkgdesc->setPlainText(liststrings.at(0));
		
		//set the package file list
		ui.pkgfiles->setPlainText(liststrings.at(1));
	}
}

//Slot for installpkg button pressed.
//It loads the install widget
void MainWindow::InstallPkgButtonPressed()
{	
	iw.show();
}

//Slot for upgradepkg button pressed.
//It loads the upgrade widget
void MainWindow::UpgradePkgButtonPressed()
{
	uw.show();
}

//Slot for removepkg button pressed
//It ask for remove package and eventually
//call the xterm with remove instructions
void MainWindow::RemovePkgButtonPressed()
{
	QString packpath="/var/log/packages/";
	packpath+=ui.pkglist->item(ui.pkglist->currentRow())->text();
	QString question="Are you sure to remove: ";
	question+=packpath;
	
	
	//ask if you want to remove that package
	QMessageBox msgBox;
	msgBox.setWindowTitle("Remove Package");
	msgBox.setText(question);
 	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
 	int res=msgBox.exec();
 	
 	if(res==QMessageBox::Yes)
 	{
 		ui.pkglist->setCurrentRow(0);
		
		//build the command to remove
		QString command="su -c removepkg\\ ";
		command+=packpath+"\0";
		
		//fork a child to make the deletion process
		pid_t pid=fork();
	
		if(pid==0)
		{
			string s=command.toStdString();
			execl("/usr/bin/xterm","xterm","-e",s.c_str(),(char*)0);
		}
 	}
 	else
 	{
 		QMessageBox::information( this,"Remove Package",
 					"Operation Cancelled!");
 	}
 	
}

//Reload the package list
//This slot is activated by refresh button
void MainWindow::RefreshButtonPressed()
{
	PKGLoading();
}

//Take informations about the packages installed in the system
//Informations are total KB packages installed, the max size package
void MainWindow::InfoButtonPressed()
{
	//-------------------------------------
	//This routine also operate a refresh!
	//-------------------------------------
	
	
	QString packpath="/var/log/packages/";
	QString filepath;
	
	int packtotalsize=0;
	QString maxpkgname="";
	int packmaxsize=0;
	
	//open the directory with package descriptor files
	QDir dirman=QDir("/var/log/packages");
	
	//loads in the lcd the package cout
	ui.lcdNumber->display((int)dirman.count());
	
	dirman.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
    	dirman.setSorting(QDir::Name);

    	QFileInfoList list = dirman.entryInfoList();
	
	//clear the list
	ui.pkglist->clear();
	
	//set the progress bar bounds
	ui.progressbar->setMaximum(list.size());

	//iterate throug the files and add them on the list
	for(int i=0; i<list.size(); ++i)
	{
		ui.progressbar->setValue(i);
		QFileInfo fileinfo = list.at(i);
		ui.pkglist->addItem(fileinfo.fileName());
		
		
		filepath=packpath+fileinfo.fileName();
	
		//file opening and reading the content
		QFile file(filepath);
		if(!file.open(QIODevice::ReadOnly))
		{
			//error
			QMessageBox msgBox(this);
			msgBox.setWindowTitle("Info Error");
			msgBox.setText( "An error occurred during information retreiving");
			msgBox.setStandardButtons(QMessageBox::Close);
			msgBox.exec();
			return;
		}
		
		QString content=QTextStream(&file).readAll();
	
		//split the file description in 2 parts separe by the string UNCOMPRESSED PACKAGE SIZE:     
		//the first is the package description, the second is the file list
		QStringList liststrings = content.split("UNCOMPRESSED PACKAGE SIZE:     ");
		
		if(liststrings.empty())
		{
			//error
			QMessageBox msgBox(this);
			msgBox.setWindowTitle("Info Error");
			msgBox.setText( "Description format error");
			msgBox.setStandardButtons(QMessageBox::Close);
			msgBox.exec();
			return;
		}
		else
		{
			//actpkgsizestr contains in the first chars the size of the uncompressed pkg
			QString actpkgsizestr=liststrings.at(1);
			int cnt=0;
			int kbsize=0;
			
			//skip not digit str
			while(!actpkgsizestr.data()[cnt].isDigit())++cnt;
			
			while(actpkgsizestr.data()[cnt].isDigit())
			{
				kbsize=(kbsize*10)+actpkgsizestr.data()[cnt].digitValue();
				++cnt;
			}
			
			packtotalsize+=kbsize;
			if(kbsize>packmaxsize)
			{
				packmaxsize=kbsize;
				maxpkgname=fileinfo.fileName();
			}
			
		}
	}
	
	
	
	//set the first package on the list and show descriptions
	ui.pkglist->setCurrentRow(0);
	ShowFileInfo(0);
	
	QString resultstr;
	QTextStream(&resultstr) << "PACKAGE SIZE INFORMATION\n\n- Total Uncompressed size: " << packtotalsize << "KB\n- Max Package Size: "<<maxpkgname <<" " << packmaxsize <<"KB";
	
	QMessageBox msgBox(this);
	msgBox.setWindowTitle("Info Memory");
	msgBox.setText( resultstr);
	msgBox.setStandardButtons(QMessageBox::Close);
	msgBox.exec();
}

//#include "mainwindow.moc"
