/* -*- mode: c++; tab-width: 4; c-basic-offset: 4 -*- */
/*
 * Copyright (C) 2005 Atmark <atmarkat _AT_ msn _DOT_ com>
 *                    AGAWA Koji <i _AT_ atty _DOT_ jp>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <qapplication.h>
#ifdef QTOPIA
#include <qpe/config.h>
#else
#include <config.h>
#endif
#include <qfile.h>
#include <qtextstream.h>

#include "configuration.h"
#include "action.h"

#if 0
const char * const Configuration::configName;

/*!
  設定ファイルに互換性が無くなり、古い設定を読むと誤動作するような場合に
  バージョンを上げてください。
  ただ単にエントリを追加したり削除しただけならバージョンを上げる必要はありません。
*/
const int Configuration::configVersion;
#endif

void saveWidgetPosition(QWidget *widget, const QString &configName, const QString &groupName)
{
#ifndef QTOPIA
	Config cf(configName, Config::File);
	cf.setGroup(groupName);
	
	bool wasMaximized = widget->isMaximized();
	
	cf.writeEntry("isMaximized", wasMaximized);
	
	if (wasMaximized)
		widget->showNormal();
	
	cf.writeEntry("x", widget->x());
	cf.writeEntry("y", widget->y());
	
	cf.writeEntry("width", widget->size().width());
	cf.writeEntry("height", widget->size().height());

	if (wasMaximized)
		widget->showMaximized();
#endif
}

void loadWidgetPosition(QWidget *widget, const QString &configName, const QString &groupName, int defX, int defY, int defWidth, int defHeight)
{
#ifndef QTOPIA
	Config cf(configName, Config::File);
	cf.setGroup(groupName);
	
	QSize size(cf.readNumEntry("width", defWidth), cf.readNumEntry("height", defHeight));
	QPoint point(cf.readNumEntry("x", defX), cf.readNumEntry("y", defY));
	
	// Use a two step approach to avoid flickering and resizing and
	// work around an issue with some WMs which causes the windows position
	// to be wrongly reported if we save later on and the user didn't change
	// the position or size of the frame.
	
	// first resize and positioning so Qt knows what we are up to
	widget->resize(size);
	widget->move(point);
	// show window, so the WM is prepared...
	widget->showNormal();
	// finally set again, so the WM explicitly gets it...
	widget->resize(size);
	widget->move(point);
	
	if (cf.readBoolEntry("isMaximized", false))
		widget->showMaximized();
#endif
}


/*!
  各設定項目のデフォルト値はここで定義します。
  設定ダイアログの「デフォルトに戻す」なども適切に処理されます。
 */
Configuration::Configuration()
	: lastDirectory(),
	  lastPlaylistDirectory(),
	  lastPlaylistFile(),
	  playOrder(PlayList::ORDER_NORMAL),
	  infoMode(SkinManager::NormalMode),
	  inputMode(PlayList::Select),
	  isBackendLoggingEnabled(false),
	  backendLogFileName("/tmp/quasar-log.txt"),
	  changePlayInfoLayoutInPortaitMode(false),
	  playlistAutoTriggerFilterInterval(500),
	  skin1("default"),
	  skin2("default"),
	  automaticallySwitchToPlayInfoEnabled(false),
	  isAskQuitingEnabled(false),
	  registerAsRemoteConMaster(false),
	  qcopBroadcastingEnabled(false),
	  isOverviewVisible(false),
#ifdef QTOPIA
	  isBatteryPlusEnabled(false),
	  batteryPlusMode4Video(),
	  batteryPlusMode4Audio(),
#endif
	  mplayerPath("mplayer"),
	  isOverlayEnabled(true),
	  overviewSplitterPosition(120),
	  isFullScreenEnabled(true),
	  isDoubleBufferingEnabled(true),
	  isCacheEnabled(true),
	  cacheSize(256),
	  isPrefetchFileEnabled(true),
	  framedropMethod(FRAMEDROP_NORMAL),
	  isAdditionalOptionsEnabled(false),
	  additionalOptions(""),
	  coverArtDirectory(""),
	  machineCategory_(MACHINE_GENERIC),
	  machineName_("")
{
	for (int i = 0; i < HOTKEY_CONTEXT_MAX; ++i)
		hotKeyMap[i] = g_ActionManager.defaultHotKey(static_cast<HotKeyContext>(i));

	setDefaultFormats();
	detectMachine();
	// GENERIC機種ではデフォルトでオーバレイを有効にしない
	if (machineCategory() == MACHINE_GENERIC) {
		isOverlayEnabled = false;
		isDoubleBufferingEnabled = false;
	}
}

Configuration::~Configuration()
{
	write();
}

//! 機種を判別します。
void Configuration::detectMachine()
{
	// まず、bvddが動いているかを確認し、動いていればSL-C3000系であると判断する
	// bvddが動いていなければ、SL-C3000系であってもGENERIC機種とする
	QFile modulesFile("/proc/modules");
	if (modulesFile.open(IO_ReadOnly)) {
		QTextStream s(&modulesFile);
		QString line;
		while ((line = s.readLine()) != QString::null) {
			if (line.startsWith("bvdd "))
				machineCategory_ = MACHINE_SLC3000;
		}
	}

	QFile productFile("/proc/deviceinfo/product");
	if (productFile.open(IO_ReadOnly)) {
		QTextStream s(&productFile);
		s >> machineName_;
		if (machineName_ == "SL-C860" || machineName_ == "SL-C760" ||
			machineName_ == "SL-C750" || machineName_ == "SL-C700")
			machineCategory_ = MACHINE_SLC700;
	} else
		machineName_ = "Unknown";
}

bool Configuration::isValidConfig() const
{
	Config cf(getConfigName(), Config::File);
	cf.setGroup(CONFIGGROUPNAME);

	bool isValid = false;
	if (cf.readNumEntry("CONFIG_VERSION", -1) == configVersion)
		isValid = true;

	return isValid;
}

// 設定ファイル内のキー名は基本的に変数名をそのまま使います。
void Configuration::write() const
{
	Config cf(getConfigName(), Config::File);

	cf.setGroup(CONFIGGROUPNAME);
	cf.writeEntry("CONFIG_VERSION", configVersion);
	cf.writeEntry("lastDirectory", lastDirectory);
	cf.writeEntry("lastPlaylistDirectory", lastPlaylistDirectory);
	cf.writeEntry("lastPlaylistFile", lastPlaylistFile);
	cf.writeEntry("PlayOrder", playOrder);
	cf.writeEntry("InfoMode", infoMode);
	cf.writeEntry("InputMode", inputMode);
	cf.writeEntry("isBackendLoggingEnabled", isBackendLoggingEnabled);
	cf.writeEntry("backendLogFileName", backendLogFileName);

	cf.writeEntry("changePlayInfoLayoutInPortaitMode", changePlayInfoLayoutInPortaitMode);
	cf.writeEntry("playlistAutoTriggerFilterInterval", playlistAutoTriggerFilterInterval);
		
	cf.writeEntry("skin1", skin1);
	cf.writeEntry("skin2", skin2);
	cf.writeEntry("automaticallySwitchToPlayInfoEnabled", automaticallySwitchToPlayInfoEnabled);
	cf.writeEntry("isAskQuitingEnabled", isAskQuitingEnabled);
	cf.writeEntry("registerAsRemoteConMaster", registerAsRemoteConMaster);
	cf.writeEntry("qcopBroadcastingEnabled", qcopBroadcastingEnabled);
	cf.writeEntry("isOverviewVisible", isOverviewVisible);
	
	cf.writeEntry("overviewSplitterPosition", overviewSplitterPosition);
				
#ifdef QTOPIA
	cf.setGroup("BatteryPlus");
	cf.writeEntry("isBatteryPlusEnabled", isBatteryPlusEnabled);
	cf.writeEntry("batteryPlusMode4Video", batteryPlusMode4Video);
	cf.writeEntry("batteryPlusMode4Audio", batteryPlusMode4Audio);
#endif

	cf.setGroup("MPlayer");
	cf.writeEntry("mplayerPath", mplayerPath);
	cf.writeEntry("isFullScreenEnabled", isFullScreenEnabled);
	cf.writeEntry("isDoubleBufferingEnabled", isDoubleBufferingEnabled);
	cf.writeEntry("framedropMethod", framedropMethod);
	cf.writeEntry("isCacheEnabled", isCacheEnabled);
	cf.writeEntry("cacheSize", cacheSize);	
	cf.writeEntry("isPrefetchFileEnabled", isPrefetchFileEnabled);
	cf.writeEntry("isAdditionalOptionsEnabled", isAdditionalOptionsEnabled);
	cf.writeEntry("additionalOptions", additionalOptions);

	cf.setGroup("Formats");
	cf.writeEntry("fileExtensions", formatFileExtensions, '^');
	cf.writeEntry("mplayerArguments", formatMPlayerArguments, '^');

	cf.setGroup("CoverArt");
	cf.writeEntry("coverArtDirectory", coverArtDirectory);

	for (int context = 0; context < HOTKEY_CONTEXT_MAX; ++context) {
		cf.setGroup(QString("HotKey_%1").arg(context));
		cf.writeEntry("count", static_cast<int>(hotKeyMap[context].count()));
		int i = 0;
		for (HotKeyMap::ConstIterator it = hotKeyMap[context].begin();
			 it != hotKeyMap[context].end();
			 ++it, ++i)
		{
			cf.writeEntry(QString::number(i),
						  QString("%1,%2").arg(it.key()).arg(it.data()));
		}
	}
}

// Modified version of Config::readListEntry that supports empty entries in a list too
QStringList Configuration::readListEntry(Config &cf, const QString &key, const QChar &sep, bool allowEmptyEntries)
{
    QString s = cf.readEntry(key);
    if (s.isEmpty())
    	return QStringList();
    else
    	return QStringList::split(sep, s, allowEmptyEntries);
}

// 設定ファイル内のキー名は基本的に変数名をそのまま使います。
void Configuration::read()
{
	Config cf(getConfigName(), Config::File);

	cf.setGroup(CONFIGGROUPNAME);

	if (cf.readNumEntry("CONFIG_VERSION") != configVersion)
		return;

	lastDirectory = cf.readEntry("lastDirectory", lastDirectory);
	lastPlaylistDirectory = cf.readEntry("lastPlaylistDirectory", lastPlaylistDirectory);
	lastPlaylistFile = cf.readEntry("lastPlaylistFile", lastPlaylistFile);
	playOrder = static_cast<PlayList::PlayOrder>(cf.readNumEntry("PlayOrder", playOrder));
	infoMode = static_cast<SkinManager::InfoMode>(cf.readNumEntry("InfoMode", infoMode));
	inputMode = static_cast<PlayList::InputMode>(cf.readNumEntry("InputMode", inputMode));
	
	isBackendLoggingEnabled = cf.readBoolEntry("isBackendLoggingEnabled", isBackendLoggingEnabled);
	backendLogFileName = cf.readEntry("backendLogFileName", backendLogFileName);

	changePlayInfoLayoutInPortaitMode = cf.readBoolEntry("changePlayInfoLayoutInPortaitMode", changePlayInfoLayoutInPortaitMode);
	playlistAutoTriggerFilterInterval = cf.readNumEntry("playlistAutoTriggerFilterInterval", playlistAutoTriggerFilterInterval);
	
	skin1 = cf.readEntry("skin", skin1);
	skin1 = cf.readEntry("skin1", skin1);
	skin2 = cf.readEntry("skin2", skin2);
	automaticallySwitchToPlayInfoEnabled = cf.readBoolEntry("automaticallySwitchToPlayInfoEnabled", automaticallySwitchToPlayInfoEnabled);
	isAskQuitingEnabled = cf.readBoolEntry("isAskQuitingEnabled", isAskQuitingEnabled);
	registerAsRemoteConMaster = cf.readBoolEntry("registerAsRemoteConMaster", registerAsRemoteConMaster);
	qcopBroadcastingEnabled = cf.readBoolEntry("qcopBroadcastingEnabled", qcopBroadcastingEnabled);
	isOverviewVisible = cf.readBoolEntry("isOverviewVisible", isOverviewVisible);
	
	overviewSplitterPosition = cf.readNumEntry("overviewSplitterPosition", overviewSplitterPosition);
		
#ifdef QTOPIA
	cf.setGroup("BatteryPlus");
	isBatteryPlusEnabled = cf.readBoolEntry("isBatteryPlusEnabled", isBatteryPlusEnabled);
	batteryPlusMode4Video = cf.readEntry("batteryPlusMode4Video", batteryPlusMode4Video);
	batteryPlusMode4Audio = cf.readEntry("batteryPlusMode4Audio", batteryPlusMode4Audio);
#endif

	cf.setGroup("MPlayer");
	mplayerPath = cf.readEntry("mplayerPath", mplayerPath);
	isFullScreenEnabled = cf.readBoolEntry("isFullScreenEnabled", isFullScreenEnabled);
	isDoubleBufferingEnabled = cf.readBoolEntry("isDoubleBufferingEnabled", isDoubleBufferingEnabled);
	framedropMethod = static_cast<FramedropMethod>(cf.readNumEntry("framedropMethod", framedropMethod));
	isCacheEnabled = cf.readBoolEntry("isCacheEnabled", isCacheEnabled);
	cacheSize = cf.readNumEntry("cacheSize", cacheSize);	
	isPrefetchFileEnabled = cf.readBoolEntry("isPrefetchFileEnabled", isPrefetchFileEnabled);
	isAdditionalOptionsEnabled = cf.readBoolEntry("isAdditionalOptionsEnabled", isAdditionalOptionsEnabled);
	additionalOptions = cf.readEntry("additionalOptions", additionalOptions);

	cf.setGroup("Formats");
	
	// use our modified readListEntry, because we need to consider empty entries too
	formatFileExtensions = readListEntry(cf, "fileExtensions", '^', true);
	formatMPlayerArguments = readListEntry(cf, "mplayerArguments", '^', true);

	if (formatFileExtensions.count() == 0 || formatMPlayerArguments.count() == 0)
		setDefaultFormats();
	else
	{
		// last entries are probably empty, so if they are, just delete them...
		// this circumvents an issue in the writeEntry implementation for QStringLists...
		if (formatFileExtensions.last().isEmpty())
		{
			formatFileExtensions.remove(formatFileExtensions.fromLast());
			formatMPlayerArguments.remove(formatMPlayerArguments.fromLast());
		}

		updateValidExtensionsList();
	}
	
	cf.setGroup("CoverArt");
	coverArtDirectory = cf.readEntry("coverArtDirectory", coverArtDirectory);

	for (int context = 0; context < HOTKEY_CONTEXT_MAX; ++context) {
		cf.setGroup(QString("HotKey_%1").arg(context));
		int count = cf.readNumEntry("count", 0);
		if (count > 0) {
			hotKeyMap[context].clear();
			for (int i = 0; i < count; ++i) {
				QString value(cf.readEntry(QString::number(i)));
				if (!value.isEmpty()) {
					QStringList args(QStringList::split(',', value));
					hotKeyMap[context].insert(args[0].toInt(), args[1].toInt());
				}
			}
		}
	}
}

void Configuration::setDefaultFormats()
{
	formatFileExtensions.clear();
	formatFileExtensions.append("ogg");
	formatFileExtensions.append("mp3");
	formatFileExtensions.append("mp4");
	formatFileExtensions.append("avi,divx");
	formatFileExtensions.append("mpg,mpeg");
	
	formatMPlayerArguments.clear();
	formatMPlayerArguments.append("-ac vorbis");
	formatMPlayerArguments.append("-ac mad -hr-mp3-seek");
	formatMPlayerArguments.append("");
	formatMPlayerArguments.append("");
	formatMPlayerArguments.append("");
	
	updateValidExtensionsList();
}

void Configuration::updateValidExtensionsList()
{
	validExtensionsList_.clear();
	for (int i = 0; i < formatFileExtensions.count(); ++i)
	{
		QStringList exts = QStringList::split(',', formatFileExtensions[i], false);
		for (int j = 0; j < exts.count(); ++j)
			validExtensionsList_ << exts[j];
	}
}

QStringList &Configuration::validExtensions()
{
	return validExtensionsList_;
}

const QString Configuration::mplayerArguments(QString fileExtension)
{
	fileExtension = fileExtension.lower();
	
	for (int i = 0; i < formatFileExtensions.count(); ++i)
		if (formatFileExtensions[i].contains(fileExtension, false))
			return formatMPlayerArguments[i];
	
	return QString::null;
}
