/*
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;

		// TODO: use QT's way of doing this
		std::istringstream ss(qPrintable(line));
		std::string perms;

		std::string temp[9];
		//0x8048000 0x8057000 13 0 0xc27e64a4 r-x 2 0 0x0 COW NC vnode /usr/local/libexec/gam_server

		// read the starting address in hex
		if(ss >> std::hex >> region.start) {

			// read the ending address and then the permissions
			if(ss >> std::hex >> region.end) {
				if(ss >> temp[0] >> temp[1] >> temp[2] >> perms) {
					if(ss >> temp[3] >> temp[4] >> temp[5] >> temp[6] >> temp[7]>> temp[8]) {
						std::string name;

						char buf[256];
						ss.getline(buf, sizeof(buf), '\n');
						name = buf;
						
						// create a bitmask for the permissions
						region.m_Permissions = 0;
						if(perms[0] == 'r') region.m_Permissions |= PROT_READ;
						if(perms[1] == 'w') region.m_Permissions |= PROT_WRITE;
						if(perms[2] == 'x') region.m_Permissions |= PROT_EXEC;
						
						region.name = QString::fromStdString(name).trimmed();

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

