/*********
*
* This file is part of BibleTime's source code, http://www.bibletime.info/.
*
* Copyright 1999-2009 by the BibleTime developers.
* The BibleTime source code is licensed under the GNU General Public License version 2.0.
*
**********/

#include "btshortcutseditor.h"
#include "btshortcutsdialog.h"

#include "frontend/displaywindow/btactioncollection.h"

#include <QTableWidget>
#include <QTableWidgetItem>
#include <QKeySequence>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QAction>
#include <QGroupBox>
#include <QRadioButton>
#include <QLabel>
#include <QSpacerItem>
#include <QPushButton>

// *************** BtShortcutsEditorItem *******************************************************************
// BtShortcutsEditorItem is the widget for the first column of the BtShortcutsEditor
// It holds extra information about the action

class BtShortcutsEditorItem : public QTableWidgetItem
{
public:
	BtShortcutsEditorItem(QAction* action);
	~BtShortcutsEditorItem();
	void commitChanges();
	QKeySequence getDefaultKeys();
	void setDefaultKeys(QKeySequence keys);
	void setFirstHotkey(QKeySequence keys);
	void setSecondHotkey(const QString& keys);
	QAction* getAction();
	void deleteHotkeys();

private:
	QAction *m_action;
	QKeySequence *m_newFirstHotkey;
	QKeySequence *m_newSecondHotkey;
	QKeySequence m_defaultKeys;
};

BtShortcutsEditorItem::BtShortcutsEditorItem(QAction* action)
	: m_action(action), m_newFirstHotkey(0), m_newSecondHotkey(0)
{
	QList<QKeySequence> list = m_action->shortcuts();
	if (list.count()>0)
		m_newFirstHotkey = new QKeySequence(list.at(0));
	if (list.count()>1)
		m_newSecondHotkey = new QKeySequence(list.at(1));
}

BtShortcutsEditorItem::~BtShortcutsEditorItem()
{
	delete m_newFirstHotkey;
	delete m_newSecondHotkey;
}

QAction* BtShortcutsEditorItem::getAction()
{
	return m_action;
}

QKeySequence BtShortcutsEditorItem::getDefaultKeys()
{
	return m_defaultKeys;
}

void BtShortcutsEditorItem::setDefaultKeys(QKeySequence keys)
{
	m_defaultKeys = keys;
}

void BtShortcutsEditorItem::setFirstHotkey(QKeySequence keys)
{
	if (m_newFirstHotkey == 0)
		m_newFirstHotkey = new QKeySequence();
	*m_newFirstHotkey = keys;
}

void BtShortcutsEditorItem::setSecondHotkey(const QString& keys)
{
	if (m_newSecondHotkey == 0)
		m_newSecondHotkey = new QKeySequence();
	*m_newSecondHotkey = QKeySequence(keys);
}

// Deletes hotkey information
void BtShortcutsEditorItem::deleteHotkeys()
{
	delete m_newFirstHotkey;
	m_newFirstHotkey = 0;
	delete m_newSecondHotkey;
	m_newSecondHotkey = 0;
}

// Moves the hotkey information into the QAction variable
void BtShortcutsEditorItem::commitChanges()
{
	QString actionName = text();
	QList<QKeySequence> list;
	if ( (m_newFirstHotkey != 0) && (*m_newFirstHotkey != QKeySequence()) )
	{
		list << *m_newFirstHotkey;
	}
	if ( (m_newSecondHotkey != 0) && (*m_newSecondHotkey != QKeySequence()) )
		list << *m_newSecondHotkey;

	if (m_action != 0)
		m_action->setShortcuts(list);
}


// ******************* BtShortcutsEditor *******************************************************

BtShortcutsEditor::BtShortcutsEditor(BtActionCollection* collection, QWidget* parent)
	: QWidget(parent), m_dlg(new BtShortcutsDialog(this)), m_table(0), m_shortcutChooser(0), m_noneButton(0), m_defaultButton(0),
		m_customButton(0), m_defaultLabelValue(0), m_currentRow(-1)
{
	init();
	addCollection(collection);
	bool ok = connect(m_dlg, SIGNAL(keyChangeRequest(const QString&)), this, SLOT(makeKeyChangeRequest(const QString&)) );
	Q_ASSERT(ok);
}

BtShortcutsEditor::BtShortcutsEditor(QWidget* parent)
	: QWidget(parent), m_table(0)
{
	init();
}

// initialize this widget
void BtShortcutsEditor::init()
{
	QVBoxLayout* vBox = new QVBoxLayout(this);
	setLayout(vBox);

	m_table = createShortcutsTable();
	vBox->addWidget(m_table);

	m_shortcutChooser = createShortcutChooser();
	vBox->addWidget(m_shortcutChooser);
}

// get the shortcut editor item from the zeroth column of the table
BtShortcutsEditorItem* BtShortcutsEditor::getShortcutsEditor(int row)
{
	QTableWidgetItem* item = m_table->item(row,0);
	BtShortcutsEditorItem* btItem = dynamic_cast<BtShortcutsEditorItem*>(item);
	return btItem;
}

// saves shortcut keys into the QAction
void BtShortcutsEditor::commitChanges()
{
	int rows = m_table->rowCount();
	for (int row=0; row<rows; row++)
	{
		BtShortcutsEditorItem* btItem = getShortcutsEditor(row);
		if (btItem != 0)
			btItem->commitChanges();
	}
}

// puts actions and shortcut keys into QTableWidget
void BtShortcutsEditor::addCollection(BtActionCollection* collection, const QString& title)
{
	QList<QAction*> actionList = collection->actions();
	int count;
	count = actionList.count();
	foreach (QAction *action, collection->actions()) 
	{
		if (action) 
		{
			int count = m_table->rowCount();
			m_table->insertRow(count);

			QString name = action->text().remove('&');
			QList<QKeySequence> keys = action->shortcuts();
			QIcon icon = action->icon();
			QKeySequence defaultKeys = collection->getDefaultShortcut(action);
			
			BtShortcutsEditorItem* item = new BtShortcutsEditorItem(action);
			item->setText(name);
			item->setIcon(icon);
			item->setDefaultKeys(defaultKeys);
			item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
			m_table->setItem(count, 0, item);

			QTableWidgetItem* item1 = new QTableWidgetItem();
			item1->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
			item1->setToolTip(tr("Select to change key"));
			if (keys.count() > 0)
				item1->setText(keys[0].toString());
			m_table->setItem(count,1,item1);

			QTableWidgetItem* item2 = new QTableWidgetItem();
			item2->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
			item2->setToolTip(tr("Select to change key"));
			if (keys.count() > 1)
				item2->setText(keys[1].toString());
			m_table->setItem(count,2,item2);
		}
	}
	m_table->sortItems(0);
	m_table->selectRow(0);
	changeRow(0,0);
}

// create the action and shortcuts table
QTableWidget* BtShortcutsEditor::createShortcutsTable()
{
	QTableWidget* table = new QTableWidget(this);
	table->setColumnCount(3);
	table->setAlternatingRowColors(true);
	table->setSelectionBehavior(QAbstractItemView::SelectRows);
	QStringList headerList;
	headerList << tr("Action\nname") << tr("First\nshortcut") << tr("Second\nshortcut");
	table->setHorizontalHeaderLabels(headerList);
	table->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
	table->horizontalHeader()->resizeSection(0, 180);
	table->horizontalHeader()->resizeSection(1, 100);
	table->horizontalHeader()->setStretchLastSection(true);
	table->verticalHeader()->setVisible(false);
	table->setShowGrid(false);
	bool ok = connect(table, SIGNAL(cellClicked(int,int)), this, SLOT(changeRow(int,int)));
	Q_ASSERT(ok);
	return table;
}

// called when a different action name row is selected
void BtShortcutsEditor::changeRow(int row, int column)
{
	BtShortcutsEditorItem* item = getShortcutsEditor(row);
	m_currentRow = row;
	QKeySequence defaultKeys = item->getDefaultKeys();

	m_defaultLabelValue->setText(defaultKeys);

	QTableWidgetItem* item1 = m_table->item(row,1);
	QString shortcut = item1->text();

	QTableWidgetItem* item2 = m_table->item(row,2);
	QString alternate = item2->text();

	QString bothKeys = shortcut;
	if (!alternate.isEmpty())
		bothKeys = bothKeys + "; " + alternate;
	m_customPushButton->setText(bothKeys);

	if ( bothKeys == defaultKeys.toString())
		m_defaultButton->setChecked(true);
	else if (bothKeys.isEmpty())
		m_noneButton->setChecked(true);
	else
		m_customButton->setChecked(true);
}

// create the area below the table where the shortcuts are edited
QWidget* BtShortcutsEditor::createShortcutChooser()
{
	QGroupBox* box = new QGroupBox(tr("Shortcut for selected action name"), this);
	box->setFlat(false);
	QVBoxLayout* vLayout = new QVBoxLayout(box);
	QHBoxLayout* hLayout = new QHBoxLayout();
	vLayout->addLayout(hLayout);
	
	m_noneButton = new QRadioButton(tr("None"), box);
	hLayout->addWidget(m_noneButton);
	bool ok = connect(m_noneButton, SIGNAL(clicked(bool)), this, SLOT(noneButtonClicked(bool)));
	Q_ASSERT(ok);

	m_defaultButton = new QRadioButton(tr("Default"), box);
	hLayout->addWidget(m_defaultButton);
	ok = connect(m_defaultButton, SIGNAL(clicked(bool)), this, SLOT(defaultButtonClicked(bool)));
	Q_ASSERT(ok);

	m_customButton = new QRadioButton(tr("Custom"), box);
	hLayout->addWidget(m_customButton);
	ok = connect(m_customButton, SIGNAL(clicked(bool)), this, SLOT(customButtonClicked(bool)));
	Q_ASSERT(ok);

	m_customPushButton = new QPushButton(box);
	m_customPushButton->setMinimumWidth(140);
	hLayout->addWidget(m_customPushButton);

	QSpacerItem* spacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
	hLayout->addItem(spacer);

	QHBoxLayout* hLayout2 = new QHBoxLayout();
	vLayout->addLayout(hLayout2);
	
	QLabel* defaultLabel = new QLabel(tr("Default key:"), box);
	hLayout2->addWidget(defaultLabel);

	m_defaultLabelValue = new QLabel(box);
	hLayout2->addWidget(m_defaultLabelValue);

	QSpacerItem* spacer2 = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
	hLayout2->addItem(spacer2);

	return box;
}


// called when the none radio button is clicked
void BtShortcutsEditor::noneButtonClicked(bool checked)
{
	if (m_currentRow < 0)
		return;
	BtShortcutsEditorItem* item = getShortcutsEditor(m_currentRow);
	m_customPushButton->setText("");
	item->deleteHotkeys();
	item->setFirstHotkey(QKeySequence(""));
	m_table->item(m_currentRow,1)->setText("");
	m_table->item(m_currentRow,2)->setText("");
}

// called when the default radio button is clicked
void BtShortcutsEditor::defaultButtonClicked(bool checked)
{
	if (m_currentRow < 0)
		return;
	BtShortcutsEditorItem* item = getShortcutsEditor(m_currentRow);
	QKeySequence defaultKeys = item->getDefaultKeys();
	item->deleteHotkeys();
	item->setFirstHotkey(defaultKeys);
	m_customPushButton->setText(defaultKeys);
	m_table->item(m_currentRow,1)->setText(defaultKeys);
	m_table->item(m_currentRow,2)->setText("");
}

// called when the custom radio button is clicked
void BtShortcutsEditor::customButtonClicked(bool checked)
{
	if (m_currentRow < 0)
		return;

	QString priKeys = m_table->item(m_currentRow,1)->text();
	QString altKeys = m_table->item(m_currentRow,2)->text();
	m_dlg->setSecondKeys(altKeys);
	m_dlg->setFirstKeys(priKeys);
	int code = m_dlg->exec();
	if (code == QDialog::Accepted)
	{
		QString newPriKeys = m_dlg->getFirstKeys();
		QString newAltKeys = m_dlg->getSecondKeys();
		if (newPriKeys == newAltKeys)
			newAltKeys = "";
		BtShortcutsEditorItem* item = getShortcutsEditor(m_currentRow);
		item->setFirstHotkey(newPriKeys);
		item->setSecondHotkey(newAltKeys);
		m_table->item(m_currentRow,1)->setText(newPriKeys);
		m_table->item(m_currentRow,2)->setText(newAltKeys);
	}
}

// complete the keyChangeRequest
void BtShortcutsEditor::makeKeyChangeRequest(const QString& keys)
{
	// signal the application that this BtShortcutsEditor wants to change this shortcut (keys)
	// The application will check other BtShortcutsEditors and put out a message if the shortcut
	// is already in use. If the user requests reassignment, the application calls the BtShortcutsEditors to 
	// reassign and set the shortcut. 
	keyChangeRequest(this, keys);
}

// used by application to complete the keyChangeRequest signal
// stores "keys" into the custom shortcuts dialog field
void BtShortcutsEditor::changeShortcutInDialog(const QString& keys)
{
	m_dlg->changeSelectedShortcut(keys);
}

// clears any shortcut keys in the table matching the specified keys
void BtShortcutsEditor::clearConflictWithKeys(const QString& keys)
{
	QString conflict;
	for (int row=0; row<m_table->rowCount(); row++)
	{
		BtShortcutsEditorItem* item = getShortcutsEditor(row);
		if (m_table->item(row,1)->text() == keys)
		{
			 m_table->item(row,1)->setText("");
			item->setFirstHotkey(QKeySequence(""));
		}
		if (m_table->item(row,2)->text() == keys)
		{
			 m_table->item(row,2)->setText("");
			item->setSecondHotkey(QKeySequence(""));
		}
	}
}

// finds any shortcut keys in the table matching the specified keys - returns the Action Name for it.
QString BtShortcutsEditor::findConflictWithKeys(const QString& keys)
{
	QString conflict;
	for (int i=0; i<m_table->rowCount(); i++)
	{
		if ( (m_table->item(i,1)->text() == keys) || (m_table->item(i,2)->text() == keys) )
			return m_table->item(i,0)->text();
	}
	return conflict;
}
