#include <QAction>
#include <QCoreApplication>

#include "psioptions.h"
#include "shortcutmanager.h"
#include "globalshortcut/globalshortcutmanager.h"


/**
 * \brief The Construtor of the Shortcutmanager
 */
ShortcutManager::ShortcutManager() : QObject(QCoreApplication::instance())
{
	// Make sure that there is at least one shortcut for sending messages
	if (shortcuts("chat.send").isEmpty()) {
		qWarning("Restoring chat.send shortcut");
		QVariantList vl;
		vl << qVariantFromValue(QKeySequence(Qt::Key_Enter)) << qVariantFromValue(QKeySequence(Qt::Key_Return));
		PsiOptions::instance()->setOption("options.shortcuts.chat.send",vl);
	}
}

/**
 * "The one and only instance" of the ShortcutManager
 */
ShortcutManager* ShortcutManager::instance_ = NULL;

/**
 * \brief The Instantiator of the Shortcutmanager
 */
ShortcutManager* ShortcutManager::instance()
{
	if(!instance_)
		instance_ = new ShortcutManager();
	return instance_;
}

/**
 * \brief get the QKeySequence associated with the keyname "name"
 * \param the shortcut name e.g. "misc.sendmessage" which is in the options xml then
 *        mirrored as options.shortcuts.misc.sendmessage
 * \return QKeySequence, the Keysequence associated with the keyname, or
 *   the first key sequence if there are multiple
 */
QKeySequence ShortcutManager::shortcut(const QString& name)
{
	QVariant variant = PsiOptions::instance()->getOption(QString("options.shortcuts.%1").arg(name));
	QList<QKeySequence> list = shortcuts(name);
	if (!list.isEmpty())
		return list.first();
	return variant.value<QKeySequence>();
}

/**
 * Users tend to get really confused when they see 'Return' in menu
 * shortcuts. 'Enter' is much more common, so we'd better use it.
 *
 * Examples:
 * 'Enter' should be preferred over 'Return' and 'Ctrl+Enter' should be
 * preferred over 'Ctrl+Return'.
 */
static bool shortcutManagerKeySequenceLessThan(const QKeySequence& k1, const QKeySequence& k2)
{
	bool e1 = k1.toString(QKeySequence::PortableText).contains("Enter");
	bool e2 = k2.toString(QKeySequence::PortableText).contains("Enter");
	return e1 && !e2;
}

/**
 * \brief get the QVariantList associated with the keyname "name"
 * \param the shortcut name e.g. "misc.sendmessage" which is in the options xml then
 *        mirrored as options.shortcuts.misc.sendmessage
 * \return List of sequences
 */
QList<QKeySequence> ShortcutManager::shortcuts(const QString& name)
{
	return readShortcutsFromOptions(name, PsiOptions::instance());
}

/**
 * \brief read the QVariantList associated with the keyname "name" in given PsiOptions
 * \param name, the shortcut name e.g. "misc.sendmessage" which is in the options xml then
 *        mirrored as options.shortcuts.misc.sendmessage
 * \param options, options instance to read from
 * \return List of sequences
 */
QList<QKeySequence> ShortcutManager::readShortcutsFromOptions(const QString& name, const PsiOptions* options)
{
	QList<QKeySequence> list;
	QVariant variant = options->getOption(QString("options.shortcuts.%1").arg(name));
	QString type = variant.typeName();
	if (type == "QVariantList") {
		foreach(QVariant variant, variant.toList()) {
			QKeySequence k = variant.value<QKeySequence>();
			if (!k.isEmpty() && !list.contains(k))
				list += k;
		}
	}
	else {
		QKeySequence k = variant.value<QKeySequence>();
		if (!k.isEmpty())
			list += k;
	}
	qStableSort(list.begin(), list.end(), shortcutManagerKeySequenceLessThan);
	return list;
}

/**
 * \brief this function connects the Key or Keys associated with the keyname "path" with the slot "slot"
 *        of the Widget "parent"
 * \param path, the shortcut name e.g. "misc.sendmessage" which is in the options xml then
 *        mirrored as options.shortcuts.misc.sendmessage
 * \param parent, the widget to which the new QAction should be connected to
 * \param slot, the SLOT() of the parent which should be triggered if the KeySequence is activated
 */
void ShortcutManager::connect(const QString& path, QObject* parent, const char* slot)
{
	if (parent == NULL || slot == NULL)
		return;

	if (!path.startsWith("global.")) {
		QList<QKeySequence> shortcuts = ShortcutManager::instance()->shortcuts(path);

		if (!shortcuts.isEmpty()) {
			bool appWide = path.startsWith("appwide.");
			QAction* act = new QAction(parent);
			act->setShortcuts(shortcuts);
			act->setShortcutContext(appWide ?
			                        Qt::ApplicationShortcut : Qt::WindowShortcut);
			if (parent->isWidgetType())
				((QWidget*) parent)->addAction(act);
			parent->connect(act, SIGNAL(triggered()), slot);
		}
	}
	else {
		foreach(QKeySequence sequence, ShortcutManager::instance()->shortcuts(path)) {
			if (!sequence.isEmpty()) {
				GlobalShortcutManager::instance()->connect(sequence, parent, slot);
			}
		}
	}
}
