/*
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 "CommentServer.h"
#include "Debugger.h"
#include "DebuggerCoreInterface.h"
#include "Configuration.h"
#include "Instruction.h"

#include <QString>

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
CommentServer::CommentServer() {
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
CommentServer::~CommentServer() {
}

//------------------------------------------------------------------------------
// Name: setComment(edb::address_t address,  const QString &comment)
// Desc: 
//------------------------------------------------------------------------------
void CommentServer::setComment(edb::address_t address, const QString &comment) {
	m_CustomComments[address] = comment;
}

//------------------------------------------------------------------------------
// Name: clear()
// Desc: 
//------------------------------------------------------------------------------
void CommentServer::clear() {
	m_CustomComments.clear();
}

// a call can be anyhere from 2 to 7 bytes long depends on if there is a Mod/RM byte
// or a SIB byte, etc
// this is ignoring prefixes, fortunately, no calls have mandatory prefixes
// TODO: this is a arch specific concept
#define CALL_MAX_SIZE 7
#define CALL_MIN_SIZE 2

//------------------------------------------------------------------------------
// Name: resolveFunctionCall(edb::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
QString CommentServer::resolveFunctionCall(edb::address_t address, bool &ok) const {
	QString ret;
	
	ok = false;

	// ok, we now want to locate the instruction before this one
	// so we need to look back a few bytes
	quint8 buffer[edb::Instruction::MAX_SIZE];

	// TODO: portability warning, makes assumptions on the size of a call
	// TODO: don't make direct use of OP_CALL test, this is implementation of Instruction
	//       specific
	if(edb::v1::debuggerBase->readBytes(address - CALL_MAX_SIZE, buffer, sizeof(buffer))) {
		for(int i = (CALL_MAX_SIZE - CALL_MIN_SIZE); i >= 0; --i) {
			edb::Instruction insn(buffer + i, sizeof(buffer) - i, 0, std::nothrow);
			if(insn.valid() && insn.type() == edb::Instruction::OP_CALL) {
				const QString symname = edb::v1::findFunctionSymbol(address);
				if(!symname.isEmpty()) {
					ret = QObject::tr("return to %1 <%2>").arg(edb::v1::formatPointer(address)).arg(symname);
				} else {
					ret = QObject::tr("return to %1").arg(edb::v1::formatPointer(address));
				}
				ok = true;
				break;
			}
		}
	}

	return ret;
}

//------------------------------------------------------------------------------
// Name: resolveString(edb::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
QString CommentServer::resolveString(edb::address_t address, bool &ok) const {
	
	const int minStringLength = edb::v1::config().minStringLength;
	QString ret;
	ok = false;
	
	int stringLen;
	QString temp;
	if((ok = edb::v1::getASCIIStringAtAddress(address, temp, minStringLength, 256, stringLen))) {
		ret = QObject::tr("ASCII \"%1\"").arg(temp);
	} else if((ok = edb::v1::getUTF16StringAtAddress(address, temp, minStringLength, 256, stringLen))) {
		ret = QObject::tr("UTF16 \"%1\"").arg(temp);
	}

	return ret;
}

#include <cstdio>

//------------------------------------------------------------------------------
// Name: getComment(edb::address_t address, int size) const
// Desc: 
//------------------------------------------------------------------------------
QString CommentServer::getComment(edb::address_t address, int size) const {

	QString ret;
	bool ok;
	
	// if the view is currently looking at words which are a pointer in size
	// then see if it points to anything...
	if(size == sizeof(edb::address_t)) {
		edb::address_t value;
		if(edb::v1::debuggerBase->readBytes(address, &value, sizeof(value))) {
	
			QMap<edb::address_t, QString>::const_iterator it = m_CustomComments.find(value);
			if(it != m_CustomComments.end()) {
				ret = it.value();
			} else {
				ret = resolveFunctionCall(value, ok);
				if(!ok) {
					ret = resolveString(value, ok);
				}
			}
		}
	}
	
	return ret;
}
