/*
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 "DialogBinaryString.h"
#include "DebuggerCoreInterface.h"
#include "MemoryRegions.h"
#include "Debugger.h"
#include "ByteShiftArray.h"
#include "Util.h"
#include <QVector>

//------------------------------------------------------------------------------
// Name: DialogBinaryString(QWidget *parent)
// Desc: constructor
//------------------------------------------------------------------------------
DialogBinaryString::DialogBinaryString(QWidget *parent) : QDialog(parent) {
	ui.setupUi(this);
	
	ui.progressBar->setValue(0);
	ui.listWidget->clear();
}

//------------------------------------------------------------------------------
// Name: doFind()
// Desc: 
//------------------------------------------------------------------------------
void DialogBinaryString::doFind() {

	const QByteArray b = ui.binaryString->value();
	ui.listWidget->clear();

	const int sz = b.size();
	if(sz != 0) {
		ByteShiftArray bsa(sz);

		edb::v1::memoryRegions().sync();
		const QVector<MemoryRegions::Region> regions = edb::v1::memoryRegions().regions();
		const edb::address_t pageSize = edb::v1::debuggerBase->pageSize();

		int i = 0;
		foreach(MemoryRegions::Region region, regions) {

			bsa.clear();
			
			// a short circut for speading things up
			if(ui.chkSkipNoAccess->isChecked() && !region.accessible()) {
				ui.progressBar->setValue(CalculatePercentage(++i, regions.size(), region.size(), region.size()));
				continue;
			}
			
			const edb::address_t size_in_pages = region.size() / pageSize;
			quint8 *pages = new quint8[size_in_pages * pageSize];
			const quint8 *const pages_end = pages + region.size();
			
			if(edb::v1::debuggerBase->readPages(region.start, pages, size_in_pages)) {
				const quint8 *p = pages;
				QString temp;
				while(p != pages_end) {
					// shift in the next byte
					bsa.shl();
					bsa[sz - 1] = *p;
									
					// compare values..
					if(std::memcmp(bsa.data(), b.constData(), sz) == 0) {
						const edb::address_t addr = (p - pages + region.start) - sz + 1;
						const edb::address_t align = 1 << (ui.cmbAlignment->currentIndex() + 1);
						
						if(!ui.chkAlignment->isChecked() || (addr % align) == 0) {
							ui.listWidget->addItem(temp.sprintf(EDB_FMT_PTR, addr));
						}
					}
					
					ui.progressBar->setValue(CalculatePercentage(i, regions.size(), p - pages, region.size()));

					++p;
				}
			}
			
			releaseArray(pages);
			
			++i;
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_btnFind_clicked()
// Desc: find button event handler
//------------------------------------------------------------------------------
void DialogBinaryString::on_btnFind_clicked() {

	ui.btnFind->setEnabled(false);
	ui.progressBar->setValue(0);
	doFind();
	ui.progressBar->setValue(100);
	ui.btnFind->setEnabled(true);
}

//------------------------------------------------------------------------------
// Name: on_listWidget_itemDoubleClicked(QListWidgetItem *item)
// Desc: follows the found item in the data view
//------------------------------------------------------------------------------
void DialogBinaryString::on_listWidget_itemDoubleClicked(QListWidgetItem *item) {
	bool ok;
	const QString s = item->text();
	const edb::address_t addr = s.toULongLong(&ok, 16);
	if(ok) {
		edb::v1::dumpData(addr);
	}
}
