/* This file is part of the KDE Linux Kernel Configurator
   Copyright (c) 2001 Malte Starostik <malte@kde.org>

   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; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

// $Id: configuration.cpp,v 1.30 2003/02/12 00:51:56 staikos Exp $

#include <qlabel.h>
#include <qlayout.h>
#include <qpushbutton.h>
#include <qsplitter.h>
#include <qtextstream.h>
#include <qtimer.h>

#include <kapplication.h>
#include <kcombobox.h>
#include <kfiledialog.h>
#include <kglobal.h>
#include <khtml_part.h>
#include <klistview.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kurlrequester.h>
#include <kparts/browserextension.h>
#include <kiconloader.h>

#include "configparser.h"
#include "configlist.h"
#include "configuration.moc"

using namespace Config;

Configuration::Configuration(QWidget *parent, const char *name)
	: ConfigurationBase(parent, name),
	  m_helpTimer(0)
{
	QVBoxLayout *lay = new QVBoxLayout(m_helpPane);
	m_helpView = new KHTMLPart(m_helpPane);
	lay->addWidget(m_helpView->widget());
	connect(m_helpView->browserExtension(),
		SIGNAL(openURLRequestDelayed(const KURL &, const KParts::URLArgs &)),
		SLOT(slotURLRequest(const KURL &, const KParts::URLArgs &)));

	m_kernelRoot->setMode(KFile::Directory | KFile::LocalOnly);

	m_parser = new Config::Parser();
	load();
}

Configuration::~Configuration()
{
	delete m_parser;
}

void Configuration::load()
{
	m_kernelRoot->setURL(m_parser->kernelRoot());
	slotUpdateArchs();
}

void Configuration::slotUpdateArchs()
{
	QStringList archs = m_parser->availableArchs(m_kernelRoot->url());
	m_archCombo->clear();
	m_archCombo->insertStringList(archs);

	int index = archs.findIndex(m_parser->arch());
	if (index != -1)
		m_archCombo->setCurrentItem(index);
	slotParseConfig();
}

void Configuration::save()
{
	QString configFile = QDir::cleanDirPath(m_configFile);
	bool writable;
	if (QFile::exists(configFile))
	{
		QFileInfo fi(configFile);
		if ((writable = fi.isWritable()))
		{
			if (KMessageBox::warningYesNo(this, i18n(
				"Are you sure you want to overwrite your existing kernel "
				"configuration in %1?").arg(configFile)) != KMessageBox::Yes)
				return;
		}
	}
	else
	{
		QFileInfo fi(m_parser->kernelRoot());
		writable = fi.isWritable();
	}
	if (writable)
		saveConfig(m_configFile);
	else if (KMessageBox::questionYesNo(this, i18n(
		"You do not have sufficient permissions to write to %1\n"
		"Do you want to write the configuration to a different "
		"file instead?").arg(configFile)) == KMessageBox::Yes)
		slotSaveAs();
}

void Configuration::defaults()
{
	if (!m_parser->root())
		return;

	if (KMessageBox::questionYesNo(this, i18n(
		"Do you really want to reset all kernel "
		"options to their default values?")) == KMessageBox::Yes)
		loadConfig(defaultConfig());
}

void Configuration::slotParseConfig()
{
	m_configList->clear();
	if (m_parser->parseConfig(m_kernelRoot->url(), m_archCombo->currentText()))
	{
		QString configFile = m_parser->kernelRoot() + "/.config";
		if (!QFile::exists(configFile))
			configFile = defaultConfig();
		loadConfig(configFile);
		m_configList->show();
		m_loadButton->setEnabled(true);
		m_saveButton->setEnabled(true);
	}
	else
	{
		QString message;
		for (ErrorList::ConstIterator it = m_parser->errors().begin();
			it != m_parser->errors().end();
			++it)
		{
			if ((*it).file().isEmpty())
				message += i18n("Error message", "<p>%1</p>").arg((*it).message());
			else
			{
				QString pad;
				pad.fill(' ', (*it).pos());
				message += i18n(
					"<p>%1, line %2:</p>\n"
					"<pre>%3<span style=\"color: red\">%4</span>%5\n"
					"%6^ %7</pre>")
					.arg((*it).file())
					.arg((*it).line())
					.arg((*it).text().left((*it).pos()))
					.arg((*it).text().mid((*it).pos(), (*it).length()))
					.arg((*it).text().mid((*it).pos() + (*it).length()))
					.arg(pad)
					.arg((*it).message());
			}
		}
		message += i18n(
			"<p>Either your kernel sources contain invalid configuration "
			"rules or you just found a bug in the KDE Kernel Configurator.</p>");
		QFile f(locate("data", "kcmlinuz/data/error.html"));
		QString html;
		if (f.open(IO_ReadOnly))
		{
			QTextStream str(&f);
			html = str.read();
		}
		else
			html = "<html><head></head><body><h1>%1</h1>"
				   "<p><b>%1</b></p>%1</body></html>";
		m_helpView->begin(dataDir());
		m_helpView->write(html
			.arg(i18n("Sorry"))
			.arg(m_parser->errors().count() == 1 ? 
				i18n("The kernel configuration could not be read due to the following error:") :
				i18n("The kernel configuration could not be read due to the following errors:"))
			.arg(message));
		m_helpView->end();
		m_configList->hide();
		m_loadButton->setEnabled(false);
		m_saveButton->setEnabled(false);
	}
}

void Configuration::slotSelected()
{
	if (!m_helpTimer)
	{
		m_helpTimer = new QTimer(this);
		connect(m_helpTimer, SIGNAL(timeout()), SLOT(slotDelayedHelp()));
	}
	if (m_helpTimer->isActive())
		m_helpTimer->stop();
	m_helpTimer->start(500, true);
}

void Configuration::slotConfigChanged()
{
	emit changed(true);
}

void Configuration::slotURLRequest(const KURL &url, const KParts::URLArgs &)
{
	if (url.protocol() == "mailto")
		kapp->invokeMailer(url);
	else
		kapp->invokeBrowser(url.url());
}

void Configuration::slotDelayedHelp()
{
	if (m_helpPane->isVisibleTo(this))
	{
		QString help;
		if (m_configList->selectedItem())
			help = static_cast<ConfigListItem *>(m_configList->selectedItem())->help();
		if (help.isEmpty())
		{
			if (m_showingIntro)
				return; // Nothing to do
			m_showingIntro = true;
			if (m_intro.isEmpty())
			{
				QFile f(locate("data", "kcmlinuz/data/intro.html"));
				if (f.open(IO_ReadOnly))
				{
					KIconLoader *kil = KGlobal::instance()->iconLoader();
					QTextStream str(&f);
					m_intro = str.read()
						.arg(i18n("Linux Kernel Configurator"))
						.arg(i18n("Option is disabled"))
						.arg(i18n("Option is enabled"))
						.arg(i18n("Compiled as a module"))
						.arg(kil->iconPath("idea", KIcon::Small))
						.arg(i18n("Information"));
				}
			}
			help = m_intro;
		}
		else
			m_showingIntro = false;

		m_helpView->begin(dataDir());
		m_helpView->write(help);
		m_helpView->end();
	}
}

void Configuration::slotLoadFrom()
{
	QString file = KFileDialog::getOpenFileName();
	if (!file.isEmpty())
		loadConfig(file);
}

void Configuration::slotSaveAs()
{
	QString file = KFileDialog::getSaveFileName();
	if (!file.isEmpty())
		saveConfig(file);
}

void Configuration::keyPressEvent(QKeyEvent *e)
{
	// Don't let return/enter in the URL requester close this dialog
	// if started via kcmshell.
	if ((e->key() == Key_Return || e->key() == Key_Enter) &&
		m_kernelRoot->hasFocus())
		e->accept();
	else
		ConfigurationBase::keyPressEvent(e);
}

const QString &Configuration::dataDir()
{
	if (m_dataDir.isEmpty())
		m_dataDir = locate("data", "kcmlinuz/data/");
	return m_dataDir;
}

QString Configuration::defaultConfig() const
{
	return QDir::cleanDirPath(QString::fromLatin1("%1/arch/%2/defconfig")
		.arg(m_parser->kernelRoot())
		.arg(m_parser->arch()));
}

void Configuration::loadConfig(const QString &fileName)
{
	m_showingIntro = false;
	if (m_parser->readConfig(fileName))
	{
		ConfigListItem *rootItem = static_cast<ConfigListItem *>(m_configList->firstChild());
		if (!rootItem)
			rootItem = new ConfigListItem(m_configList, m_parser->root());
		rootItem->initialize();
		rootItem->setOpen(true);
		slotDelayedHelp(); // Reset help text
		m_configFile = fileName == defaultConfig() ?
			m_parser->kernelRoot() + "/.config" : fileName;
		m_config->setText(QDir::cleanDirPath(m_configFile));
		emit changed(false);
	}
}

void Configuration::saveConfig(const QString &fileName)
{
	if (m_parser->writeConfig(fileName))
	{
		QString info;
		if (QDir::cleanDirPath(fileName) == QDir::cleanDirPath(m_parser->kernelRoot() + "/.config"))
		{
			if (m_parser->writeHeader(m_parser->kernelRoot() + "/include/linux/autoconf.h"))
			{
				if (!QFile::exists(m_parser->kernelRoot() + "/.hdepend") ||
					m_parser->symbol("CONFIG_MODVERSION") == "y")
					info = i18n(
						"Your kernel configuration has been saved.\n"
						"You need to run 'make symlinks dep' now.");
				else
					info = i18n(
						"Your kernel configuration has been saved.\n"
						"You may run 'make bzImage', 'make bzDisk' or "
						"'make install' now.");
			}
			else
				info = i18n(
					"Your kernel configuration has been saved.\n"
					"The file %1, which is needed for the kernel build "
					"could not be written though.\n"
					"Please run 'make oldconfig'.")
					.arg(QDir::cleanDirPath(m_parser->kernelRoot() + "/include/linux/autoconf.h"));
		}
		else
			info = i18n("Your kernel configuration has been saved.");
		KMessageBox::information(this, info);
	}
	else
		KMessageBox::sorry(this, i18n(
			"The kernel configuration could not be saved."));
	emit changed(false);
}

// vim: ts=4 sw=4 noet
