/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "Bookmarks.h"
#include <QMenu>
#include <QMessageBox>
#include <QDockWidget>
#include <QMainWindow>
#include <QGridLayout>
#include <QPushButton>
#include <QListWidget>
#include <QInputDialog>
#include <QSignalMapper>
#include <QShortcut>
#include "Debugger.h"
#include "Expression.h"

//------------------------------------------------------------------------------
// Name: Bookmarks(
// Desc: 
//------------------------------------------------------------------------------
Bookmarks::Bookmarks() : m_Menu(0), m_ButtonAdd(0), m_ButtonDel(0), m_ButtonClear(0), m_List(0) {
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void Bookmarks::add() {
	bool ok;
    const QString text = QInputDialog::getText(edb::v1::debuggerUI, "Bookmark Address", "Address:", QLineEdit::Normal, QString(), &ok);
	
	if (ok && !text.isEmpty()) {
		Expression<edb::address_t> expr(text, &edb::v1::getVariable, 0, &edb::v1::getDWORD, 0);
		try {
			const edb::address_t address = expr.evaluateExpression();
			if(!m_Entries.contains(address)) {
				m_List->addItem(QString().sprintf(EDB_FMT_PTR, address));
				m_Entries.push_back(address);
			}
		} catch(const Error &err) {
			QMessageBox::information(edb::v1::debuggerUI, "Error In Address Expression!", err.what());
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void Bookmarks::del() {
	
	QListWidgetItem *const item = m_List->takeItem(m_List->currentRow());
	if(item != 0) {
		bool ok;
		const QString s = item->text();
#if defined(EDB_X86)
		const edb::address_t address = s.left(8).toULong(&ok, 16);
#elif defined(EDB_X86_64)
		const edb::address_t address = s.left(16).toULong(&ok, 16);
#endif
		m_Entries.removeAt(m_Entries.indexOf(address));
		delete item;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void Bookmarks::clear() {
	m_List->clear();
	m_Entries.clear();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void Bookmarks::itemDoubleClicked(QListWidgetItem *item) {
	bool ok;
	if(item != 0) {
		const QString s = item->text();
#if defined(EDB_X86)
		const edb::address_t addr = s.left(8).toULong(&ok, 16);
#elif defined(EDB_X86_64)
		const edb::address_t addr = s.left(16).toULong(&ok, 16);
#endif
		if(ok) {
			edb::v1::jumpToAddress(addr);
		}
	}
}

//------------------------------------------------------------------------------
// Name: doShortcut(int index)
// Desc: 
//------------------------------------------------------------------------------
void Bookmarks::doShortcut(int index) {

	QListWidgetItem *const item = m_List->item(index);
	
	if(item != 0) {
		emit itemDoubleClicked(item);
	}
}

//------------------------------------------------------------------------------
// Name: menu(QWidget *parent)
// Desc: 
//------------------------------------------------------------------------------
QMenu *Bookmarks::menu(QWidget *parent) {
	if(m_Menu == 0) {
	
		// if we are dealing with a main window (and we are...)
		// add the dock object
		QMainWindow *const mainWindow = dynamic_cast<QMainWindow *>(parent);
		if(mainWindow != 0) {
			
			// boiler plate button stuff
			m_ButtonAdd		= new QPushButton("Add");
			m_ButtonDel		= new QPushButton("Del");
			m_ButtonClear	= new QPushButton("Clear");
			m_List			= new QListWidget;

			// events..
			connect(m_ButtonAdd, SIGNAL(clicked()), this, SLOT(add()));
			connect(m_ButtonDel, SIGNAL(clicked()), this, SLOT(del()));
			connect(m_ButtonClear, SIGNAL(clicked()), this, SLOT(clear()));
			connect(m_List, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(itemDoubleClicked(QListWidgetItem *)));

			// layouts
			QBoxLayout *const buttonLayout = new QBoxLayout(QBoxLayout::LeftToRight);
			buttonLayout->addWidget(m_ButtonAdd);
			buttonLayout->addWidget(m_ButtonDel);
			buttonLayout->addWidget(m_ButtonClear);

			QBoxLayout *const layout = new QBoxLayout(QBoxLayout::TopToBottom);
			layout->addWidget(m_List);
			layout->addLayout(buttonLayout);

			QWidget *const widget = new QWidget(parent);
			widget->setLayout(layout);

			// make the dock widget and _name_ it, it is important to name it so
			// that it's state is saved in the GUI info
			QDockWidget *const dockWidget = new QDockWidget("Bookmarks", parent);
			dockWidget->setObjectName(QString::fromUtf8("Bookmarks"));
			dockWidget->setWidget(widget);

			// add it to the dock
			mainWindow->addDockWidget(Qt::RightDockWidgetArea, dockWidget);

			// make the menu and add the show/hide toggle for the widget
			m_Menu = new QMenu("Bookmarks", parent);
			m_Menu->addAction(dockWidget->toggleViewAction());
			
			m_SignalMapper = new QSignalMapper(this);
			
			QString temp;
			for (int i = 0; i < 10; ++i) {
			
				// create an action and attach it to the signal mapper
				QShortcut *const action = new QShortcut(QKeySequence(temp.sprintf("Ctrl+%d", i)), mainWindow);
								
				if(i == 0) {
					m_SignalMapper->setMapping(action, 9);
				} else {
					m_SignalMapper->setMapping(action, i - 1);
				}
				
				connect(action, SIGNAL(activated()), m_SignalMapper, SLOT(map()));
			}
			
			// connect the mapped signals to a single signal with a param
			connect(m_SignalMapper, SIGNAL(mapped(int)), this, SIGNAL(signalShortcut(int)));
			
			// connect the parametized signal to the slot..phew finally
			connect(this, SIGNAL(signalShortcut(int)), this, SLOT(doShortcut(int)));
		}
	}

	return m_Menu;
}

//------------------------------------------------------------------------------
// Name: doMenu()
// Desc: 
//------------------------------------------------------------------------------
void Bookmarks::doMenu() {

	
}

Q_EXPORT_PLUGIN2(Bookmarks, Bookmarks)
