/*
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 "MemoryRegions.h"
#include "SymbolManager.h"
#include "DebuggerCoreInterface.h"
#include "Util.h"
#include "State.h"

//------------------------------------------------------------------------------
// Name: MemoryRegions(SymbolManager &sym)
// Desc: constructor
//------------------------------------------------------------------------------
MemoryRegions::MemoryRegions(SymbolManager &sym) : QAbstractItemModel(0), m_PID(0), m_SymbolManager(sym) {
}

//------------------------------------------------------------------------------
// Name: ~MemoryRegions()
// Desc: destructor
//------------------------------------------------------------------------------
MemoryRegions::~MemoryRegions() {
}

//------------------------------------------------------------------------------
// Name: setPID(edb::pid_t pid)
// Desc: 
//------------------------------------------------------------------------------
void MemoryRegions::setPID(edb::pid_t pid) {
	m_PID = pid;
	m_Regions.clear();
	sync();
}

//------------------------------------------------------------------------------
// Name: clear()
// Desc: 
//------------------------------------------------------------------------------
void MemoryRegions::clear() {
	m_PID = 0;
	m_Regions.clear();
}

#include <QMessageBox>

//------------------------------------------------------------------------------
// Name: sync()
// Desc: reads a memory map file line by line
//------------------------------------------------------------------------------
void MemoryRegions::sync() {

	m_Regions.clear();
	
	if(m_PID != 0) {
		HANDLE ph = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, m_PID);
		if(ph != 0) {		
			edb::address_t addr	= edb::nullptr;
			LPVOID last_base	= reinterpret_cast<LPVOID>(-1);
			
			Q_FOREVER {
				MEMORY_BASIC_INFORMATION info;
				VirtualQueryEx(ph, reinterpret_cast<LPVOID>(addr), &info, sizeof(info));
				
				if(last_base == info.BaseAddress) {
					break;
				}
				
				last_base = info.BaseAddress;

				// for now we ignore modifiers
				info.Protect &= ~(PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE);

				if(info.Protect != 0 && info.Protect != PAGE_NOACCESS) {
					MemRegion region;
					region.start			= reinterpret_cast<edb::address_t>(info.BaseAddress);
					region.end				= reinterpret_cast<edb::address_t>(info.BaseAddress) + info.RegionSize;
					region.base				= reinterpret_cast<edb::address_t>(info.AllocationBase);
					region.m_Permissions	= info.Protect;
			
					m_Regions.push_back(region);
				}

				addr += info.RegionSize;
			}
		
			CloseHandle(ph);
		}
	}
	reset();
}

//------------------------------------------------------------------------------
// Name: findRegion(edb::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
bool MemoryRegions::findRegion(edb::address_t address) const {
	foreach(const MemRegion &i, m_Regions) {
		if(address >= i.start && address < i.end) {
			return true;
		}
	}
	return false;
}

//------------------------------------------------------------------------------
// Name: findRegion(edb::address_t address, MemRegion &region) const
// Desc: 
//------------------------------------------------------------------------------
bool MemoryRegions::findRegion(edb::address_t address, MemRegion &region) const {

	foreach(const MemRegion &i, m_Regions) {
		if(address >= i.start && address < i.end) {
			region = i;
			return true;
		}
	}
	return false;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QVariant MemoryRegions::data(const QModelIndex &index, int role) const {

	if(index.isValid() && role == Qt::DisplayRole) {
	
		const MemRegion &region = m_Regions[index.row()];
	
		switch(index.column()) {
		case 0:
			return QString("%1").arg(region.start, EDB_MAX_HEX, 16, QChar('0'));
		case 1:
			return QString("%1").arg(region.end, EDB_MAX_HEX, 16, QChar('0'));
		case 2:
			return QString("%1%2%3").arg(region.readable() ? 'r' : '-').arg(region.writable() ? 'w' : '-').arg(region.executable() ? 'x' : '-' );
		case 3:
			return region.name;
		}
	}
	
	return QVariant();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QModelIndex MemoryRegions::index(int row, int column, const QModelIndex &parent) const {
	Q_UNUSED(parent);

	if(row >= m_Regions.size()) {
		return QModelIndex();
	}

	if(column >= 4) {
		return QModelIndex();
	}
	
	return createIndex(row, column, const_cast<MemRegion *>(&m_Regions[row]));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QModelIndex MemoryRegions::parent(const QModelIndex &index) const {
	Q_UNUSED(index);
	return QModelIndex(); 
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
int MemoryRegions::rowCount(const QModelIndex &parent) const {
	Q_UNUSED(parent);
	return m_Regions.size();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
int MemoryRegions::columnCount(const QModelIndex &parent) const {
	Q_UNUSED(parent);
	return 4;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QVariant MemoryRegions::headerData(int section, Qt::Orientation orientation, int role) const {
	if(role == Qt::DisplayRole && orientation == Qt::Horizontal) {
		switch(section) {
		case 0:
			return tr("Start Address");
		case 1:
			return tr("End Address");
		case 2:
			return tr("Permissions");
		case 3:
			return tr("Name");
		}	
	}
	
	return QVariant();
}

