/*
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 "DialogFunctions.h"
#include "DebuggerCoreInterface.h"
#include "Debugger.h"
#include "MemoryRegions.h"
#include "Util.h"
#include "Configuration.h"
#include "Instruction.h"
#include "State.h"
#include "ArchProcessor.h"

#include <QHeaderView>
#include <QMessageBox>
#include <QVector>
#include <QSortFilterProxyModel>
#include <QMap>

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DialogFunctions::DialogFunctions(QWidget *parent) : QDialog(parent) {
	ui.setupUi(this);
	ui.tableView->verticalHeader()->hide();
	ui.tableView->horizontalHeader()->setStretchLastSection(true);
	
	ui.tableWidget->verticalHeader()->hide();
	ui.tableWidget->horizontalHeader()->setStretchLastSection(true);
	
	m_FilterModel = new QSortFilterProxyModel(this);
	connect(ui.txtSearch, SIGNAL(textChanged(const QString &)), m_FilterModel, SLOT(setFilterFixedString(const QString &)));
}

//------------------------------------------------------------------------------
// Name: on_tableWidget_cellDoubleClicked (int row, int column)
// Desc: follows the found item in the data view
//------------------------------------------------------------------------------
void DialogFunctions::on_tableWidget_cellDoubleClicked (int row, int column) {
	Q_UNUSED(column);
	
	bool ok;
	
	QTableWidgetItem * const item =  ui.tableWidget->item(row, 0);
	const QString s = item->text();
	
	// TODO: remove hard coded 8
	const edb::address_t addr = s.left(8).toUInt(&ok, 16);
	if(ok) {
		Debugger::jumpToAddress(addr);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogFunctions::showEvent(QShowEvent *) {
	m_FilterModel->setFilterKeyColumn(3);
	m_FilterModel->setSourceModel(&Debugger::memoryRegions());
	ui.tableView->setModel(m_FilterModel);
	
	ui.progressBar->setValue(0);
	ui.tableWidget->setRowCount(0);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DialogFunctions::isThunk(edb::address_t address) const {
	uint8_t buf[Instruction::MAX_SIZE];
	int buf_size = sizeof(buf);
	bool ret = false;
	if(Debugger::getInstructionBytes(address, buf, buf_size)) {
		Instruction insn(buf, buf_size, address, std::nothrow);
		if(insn.valid()) {
			ret = insn.type() == Instruction::OP_JMP;
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogFunctions::on_btnFind_clicked() {
	const QItemSelectionModel *const selModel = ui.tableView->selectionModel();
	const QModelIndexList sel = selModel->selectedRows();

	edb::address_t start_address	= 0;
	edb::address_t end_address	= 0;
	
	QString str;
	QMap<edb::address_t, int> resultSet;
	ui.tableWidget->setRowCount(0);

	if(sel.size() == 0) {
		QMessageBox::information(this, "No Region Selected", "You must select a region which is to be scanned for functions.");
	}

	foreach(const QModelIndex selected_item, sel) {

		const QModelIndex index = m_FilterModel->mapToSource(selected_item);
		const MemoryRegions::Region *const region = reinterpret_cast<const MemoryRegions::Region *>(index.internalPointer());
		
		ui.progressBar->setValue(0);
		
		if(region != 0) {

			start_address	= region->start;
			end_address		= region->end;
			
			State state;

			const edb::address_t orig_start	= start_address;
			const edb::address_t howMany		= end_address - orig_start;

			// do the search for this region!
			while(start_address < end_address) {

				uint8_t buf[Instruction::MAX_SIZE];
				int buf_size = sizeof(buf);

				if(Debugger::getInstructionBytes(start_address, buf, buf_size)) {
					Instruction insn(buf, buf_size, start_address, std::nothrow);
					if(insn.valid()) {
						if(insn.type() == Instruction::OP_CALL) {

							edb::address_t ret = 0;

							Debugger::currentState().toBackup(state);
							state.eip = start_address;

							const Operand &op = insn.operand(0);
							switch(op.generalType()) {
							case Operand::TYPE_REGISTER:
							case Operand::TYPE_IMMEDIATE:
							case Operand::TYPE_ABSOLUTE:
								break;
							case Operand::TYPE_REL:
							case Operand::TYPE_EXPRESSION:
								ret = Debugger::archProcessor()->getEffectiveAddress(op, state);
								break;
							default:
								break;
							}

							if(ret != 0) {
								if(ret >= region->start && ret < region->end) {
									resultSet[ret] += 1;
								}
							}
							
							ui.progressBar->setValue(CalculatePercentage((start_address - orig_start), howMany));
						}
					}
				}
				
				++start_address;
			}
		}
		ui.progressBar->setValue(100);
		
		ui.tableWidget->setSortingEnabled(false);

		QMap<edb::address_t, int>::const_iterator it = resultSet.begin();
		while(it != resultSet.end()) {

			const int row = ui.tableWidget->rowCount();
			ui.tableWidget->insertRow(row);
			ui.tableWidget->setItem(row, 0, new QTableWidgetItem(QString().sprintf("%08x", it.key())));
			
			QTableWidgetItem *const itemCount = new QTableWidgetItem;
			itemCount->setData(Qt::DisplayRole, it.value());
			
			ui.tableWidget->setItem(row, 1, itemCount);
			if(isThunk(it.key())) {
			    ui.tableWidget->setItem(row, 2, new QTableWidgetItem("Thunk"));
			} else {
				ui.tableWidget->setItem(row, 2, new QTableWidgetItem("Standard Function"));
			}
		
			++it;
		}

		ui.tableWidget->setSortingEnabled(true);
	}
}
