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

//------------------------------------------------------------------------------
// Name: setComment(edb::address_t address, int size, const QString &comment)
// Desc: 
//------------------------------------------------------------------------------
void CommentServer::setComment(edb::address_t address, int size, const QString &comment) {
	Q_UNUSED(address);
	Q_UNUSED(size);
	Q_UNUSED(comment);
}

// 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;
	
	edb::address_t value;
	ok = false;
	// get the value that address points to (usually [esp+x])
	if(edb::v1::debuggerBase->readBytes(address, &value, sizeof(value))) {
	
		// ok, we now want to locate the instruction before this one
		// so we need to look back a few bytes
		quint8 buffer[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(value - CALL_MAX_SIZE, buffer, sizeof(buffer))) {
			for(int i = (CALL_MAX_SIZE - CALL_MIN_SIZE); i >= 0; --i) {
				Instruction insn(buffer + i, sizeof(buffer) - i, 0, std::nothrow);
				if(insn.valid() && insn.type() == Instruction::OP_CALL) {
					const QString symname = edb::v1::findFunctionSymbol(value, QString());
					if(!symname.isEmpty()) {
						ret.sprintf("Return to " EDB_FMT_PTR " <%s>", value, qPrintable(symname));
					} else {
						ret.sprintf("Return to " EDB_FMT_PTR, value);
					}
					ok = true;
					break;
				}
			}
		}
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: resolveAsciiString(edb::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
QString CommentServer::resolveAsciiString(edb::address_t address, bool &ok) const {
	QString ret;
	QString temp;
	
	ok = false;
	
	edb::reg_t value;
	if(edb::v1::debuggerBase->readBytes(address, &value, sizeof(value))) {
		int stringLen;
		ok = edb::v1::getASCIIStringAtAddress(value, temp, edb::v1::config().minAsciiStringLength, 256, stringLen);
		if(ok) {
			ret = "ASCII \"" + temp + "\"";
		}
	}
	return ret;
}

//------------------------------------------------------------------------------
// 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)) {
		ret = resolveFunctionCall(address, ok);
		if(!ok) {
			ret = resolveAsciiString(address, ok);
		}
	}
	
	return ret;
}
