/*
Copyright (C) 2006 - 2009 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"
#include "Debugger.h"
#include <sstream>
#include <string>
#include <cstring>
#include <QtGlobal>
#include <QApplication>
#include <QFile>
#include <QDebug>
#include <QTextStream>
#include <QStringList>
#include <sys/mman.h>

//------------------------------------------------------------------------------
// Name: MemoryRegions()
// Desc: constructor
//------------------------------------------------------------------------------
MemoryRegions::MemoryRegions() : QAbstractItemModel(0), m_PID(0) {
}

//------------------------------------------------------------------------------
// 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();
}

namespace {
	//------------------------------------------------------------------------------
	// Name: proccessMapLine(const QString &line, MemRegion &region)
	// Desc: parses the data from a line of a memory map file
	//------------------------------------------------------------------------------
	bool proccessMapLine(const QString &line, MemRegion &region) {

		bool ret = false;
		const QStringList items = line.split(" ", QString::SkipEmptyParts);
		if(items.size() >= 3) {
			bool ok;
			const QStringList bounds = items[0].split("-");
			if(bounds.size() == 2) {
				region.start = bounds[0].toULongLong(&ok, 16);
				if(ok) {
					region.end = bounds[1].toULongLong(&ok, 16);
					if(ok) {
						region.base = items[2].toULongLong(&ok, 16);
						if(ok) {
							const QString perms	= items[1];
							MemRegion::permissions_t permisions = 0;
							if(perms[0] == 'r') permisions |= PROT_READ;
							if(perms[1] == 'w') permisions |= PROT_WRITE;
							if(perms[2] == 'x') permisions |= PROT_EXEC;
							region.m_Permissions = permisions;

							if(items.size() >= 6) {
								region.name = items[5];
							}

							// if the region has a name, is mapped starting
							// at the begining of the file, and is executable, sounds 
							// like a module mapping!
							if(!region.name.isEmpty()) {
								if(region.base == edb::nullptr) {
									if(region.executable()) {
										edb::v1::symbolManager().loadSymbolFile(region.name, region.start);
									}
								}
							}

							ret = true;
						}
					}
				}
			}
		}
		return ret;
	}
}

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

	m_Regions.clear();

	if(m_PID != 0) {
		const QString mapFile(QString("/proc/%1/maps").arg(m_PID));

		QFile file(mapFile);
        if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
		
			QTextStream in(&file);
			QString line = in.readLine();
		
			while(!line.isNull()) {
				MemRegion region;
				if(proccessMapLine(line, region)) {
					m_Regions.push_back(region);
				}
				line = in.readLine();
			}
		}
		
		if(m_Regions.isEmpty()) {
			qDebug() << "[MemoryRegions] warning: empty memory map";
		}
	}
	reset();
}

//------------------------------------------------------------------------------
// Name: findRegion(edb::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
bool MemoryRegions::findRegion(edb::address_t address) const {
	Q_FOREACH(const MemRegion &i, m_Regions) {
		if(i.contains(address)) {
			return true;
		}
	}
	return false;
}

//------------------------------------------------------------------------------
// Name: findRegion(edb::address_t address, MemRegion &region) const
// Desc: 
//------------------------------------------------------------------------------
bool MemoryRegions::findRegion(edb::address_t address, MemRegion &region) const {
	Q_FOREACH(const MemRegion &i, m_Regions) {
		if(i.contains(address)) {
			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 edb::v1::formatPointer(region.start);
		case 1:
			return edb::v1::formatPointer(region.end);
		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 >= rowCount(parent) || column >= columnCount(parent)) {
		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();
}

