/*
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 "SymbolManager.h"
#include "Debugger.h"
#include <istream>
#include <fstream>
#include <iostream>
#include <QFileInfo>

//------------------------------------------------------------------------------
// Name: SymbolManager(const QString &symbolDirectory)
// Desc: 
//------------------------------------------------------------------------------
SymbolManager::SymbolManager(const QString &symbolDirectory) : m_SymbolDirectory(symbolDirectory) {

}

//------------------------------------------------------------------------------
// Name: loadSymbolFile(const QString &filename, BaseTypes::address_t base)
// Desc: 
//------------------------------------------------------------------------------
void SymbolManager::loadSymbolFile(const QString &filename, BaseTypes::address_t base) {

	const QFileInfo fileInfo(filename);
	QString name = fileInfo.baseName();
	if(fileInfo.completeSuffix() != "") {
		name += ".";
		name += fileInfo.completeSuffix();
	}

	if(m_SymbolFiles.contains(name) == 0) {
		if(processSymbolFile(m_SymbolDirectory + "/" + name + ".map", base)) {
			m_SymbolFiles.insert(name);
		}
	}
}

//------------------------------------------------------------------------------
// Name: findSymbol(const QString &name) const
// Desc: 
//------------------------------------------------------------------------------
const SymbolManager::Symbol *SymbolManager::findSymbol(const QString &name) const {
	QMap<QString, const Symbol *>::const_iterator it = m_SymbolsByName.find(name);
	if(it != m_SymbolsByName.end()) {
		return it.value();
	}
	return 0;
}

//------------------------------------------------------------------------------
// Name: findSymbol(BaseTypes::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
const SymbolManager::Symbol *SymbolManager::findSymbol(BaseTypes::address_t address) const {

	QMap<BaseTypes::address_t, const Symbol *>::const_iterator it = m_SymbolsByAddress.find(address);
	if(it != m_SymbolsByAddress.end()) {
		return it.value();
	}
	return 0;
}


//------------------------------------------------------------------------------
// Name: findNearSymbol(BaseTypes::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
const SymbolManager::Symbol *SymbolManager::findNearSymbol(BaseTypes::address_t address) const {

	QList<Symbol>::const_iterator it = m_Symbols.begin();
	while(it != m_Symbols.end()) {
		if(address >= it->address && address < it->address + it->size) {
			return &(*it);
		}
		++it;
	}
	return 0;
}

//------------------------------------------------------------------------------
// Name: processSymbolFile(const QString &f, BaseTypes::address_t base)
// Desc: 
//------------------------------------------------------------------------------
bool SymbolManager::processSymbolFile(const QString &f, BaseTypes::address_t base) {

	Debugger::log(QString().sprintf("loading symbols: %s", qPrintable(f)));
	std::ifstream file(qPrintable(f));
	if(file) {
		
		uint32_t sym_start;
		uint32_t sym_end;
		char sym_type;
		std::string sym_name;
		std::string date;
		std::string md5;
		std::string filename;

		if(std::getline(file, date)) {
			if(file >> md5 >> filename) {
			
				const QFileInfo fileInfo(QString(filename.c_str()));
				QString prefix = fileInfo.baseName();
				if(fileInfo.completeSuffix() != "") {
					prefix += ".";
					prefix += fileInfo.completeSuffix();
				}

				while(file >> std::hex >> sym_start >> std::hex >> sym_end >> sym_type >> sym_name) {
					Symbol sym;

					sym.file	= f;
					sym.name	= (prefix + "::" + sym_name.c_str());
					sym.address	= sym_start;
					sym.size	= sym_end;

					// fixup the base address based on where it is loaded
					if(sym.address < base) {
						sym.address += base;
					}

					m_Symbols.append(sym);

					Symbol *const p = &m_Symbols.back();

					m_SymbolsByAddress.insert(p->address, p);
					m_SymbolsByName.insert(p->name, p);
				}
			}
		}
	}
	
	return true;
}
