/*
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 "DialogReferences.h"
#include "DebuggerCoreInterface.h"
#include "PluginAPI.h"
#include "Util.h"
#include "MemoryRegions.h"
#include "Core.h"

#include <QVector>
#include <boost/scoped_array.hpp>

//------------------------------------------------------------------------------
// Name: DialogReferences(QWidget *parent)
// Desc: constructor
//------------------------------------------------------------------------------
DialogReferences::DialogReferences(PluginAPI &api, QWidget *parent) : QDialog(parent), m_API(api) {
	ui.setupUi(this);
	connect(this, SIGNAL(updateProgress(int)), ui.progressBar, SLOT(setValue(int)));
}

//------------------------------------------------------------------------------
// Name: showEvent(QShowEvent *)
// Desc: 
//------------------------------------------------------------------------------
void DialogReferences::showEvent(QShowEvent *) {
	ui.listWidget->clear();
	ui.progressBar->setValue(0);
}

//------------------------------------------------------------------------------
// Name: doFind()
// Desc: 
//------------------------------------------------------------------------------
void DialogReferences::doFind() {
	bool ok;
	const edb::address_t address	= edb::core::stringToAddress(ui.txtAddress->text(), ok);
	const edb::address_t pageSize	= m_API.debuggerCore->pageSize();
	
	if(ok) {
		m_API.memoryRegions().sync();
		const QVector<MemRegion> regions = m_API.memoryRegions().regions();

		int i = 0;
		foreach(const MemRegion &region, regions) {
			// a short circut for speading things up
			if(region.accessible() || !ui.chkSkipNoAccess->isChecked()) {

				const edb::address_t size_in_pages = region.size() / pageSize;
				boost::scoped_array<quint8> pages(new quint8[size_in_pages * pageSize]);
				const quint8 *const pages_end = &pages[0] + size_in_pages * pageSize;

				if(m_API.debuggerCore->readPages(region.start, &pages[0], size_in_pages)) {
					const quint8 *p = &pages[0];
					while(p != pages_end) {

						if(static_cast<std::size_t>(pages_end - p) < sizeof(edb::address_t)) {
							break;
						}

						if(*reinterpret_cast<const edb::address_t *>(p) == address) {
							const edb::address_t addr = p - &pages[0] + region.start;
							ui.listWidget->addItem(QString("%1").arg(addr, EDB_MAX_HEX, 16, QChar('0')));
						}

						emit updateProgress(CalculatePercentage(i, regions.size(), p - &pages[0], region.size()));
						++p;
					}
				}
			} else {
				emit updateProgress(CalculatePercentage(i, regions.size()));
			}
			++i;
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_btnFind_clicked()
// Desc: find button event handler
//------------------------------------------------------------------------------
void DialogReferences::on_btnFind_clicked() {
	ui.btnFind->setEnabled(false);
	ui.listWidget->clear();
	ui.progressBar->setValue(0);
	doFind();
	ui.progressBar->setValue(100);
	ui.btnFind->setEnabled(true);
}

//------------------------------------------------------------------------------
// Name: on_listWidget_itemDoubleClicked(QListWidgetItem *item)
// Desc: follows the foudn item in the data view
//------------------------------------------------------------------------------
void DialogReferences::on_listWidget_itemDoubleClicked(QListWidgetItem *item) {
	bool ok;
	const edb::address_t addr = edb::core::stringToAddress(item->text(), ok);
	if(ok) {
		m_API.dumpData(addr, false);
	}
}
