/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef ELFSYMBOLTABLEMANAGER_H_
#define ELFSYMBOLTABLEMANAGER_H_

#include <vector>
#include <cassert>

#include "filefragment.h"
#include "elfstringtable.h"
#include "outputfile.h"
#include "elfheader.h"
#include "e32romimage.h"
#include "elfsection.h"
#include "elfsectionmanager.h"
#include "defs.h" // String

class ElfSymbolTableManager;

class SectionNumberMapping {
public:
	SectionNumberMapping(size_t aOld, size_t aNew):
		iOld(aOld), iNew(aNew)
		{}
	
	SectionNumberMapping & operator=(const SectionNumberMapping & aSectionNumberMapping){
		iOld = aSectionNumberMapping.iOld;
		iNew = aSectionNumberMapping.iNew;
		return *this;
	}
	
	SectionNumberMapping(const SectionNumberMapping & aSectionNumberMapping){
		*this = aSectionNumberMapping;
	}

	size_t iOld;
	size_t iNew;
};

typedef std::vector< SectionNumberMapping > SectionNumberMap;

class SectionVaddrAddendMapping {
public:
	SectionVaddrAddendMapping(size_t aSectionNumber, signed int aAddend):
		iSectionNumber(aSectionNumber), iAddend(aAddend)
		{}
	
	SectionVaddrAddendMapping & operator=(const SectionVaddrAddendMapping & aSectionVaddrAddendMapping){
		iSectionNumber = aSectionVaddrAddendMapping.iSectionNumber;
		iAddend = aSectionVaddrAddendMapping.iAddend;
		return *this;
	}
	
	SectionVaddrAddendMapping(const SectionVaddrAddendMapping & SectionVaddrAddendMapping){
		*this = SectionVaddrAddendMapping;
	}

	size_t iSectionNumber;
	signed int iAddend;
};

typedef std::vector< SectionVaddrAddendMapping > SectionVaddrAddendMap;

class ElfFileSymbolFragments {
public:
	ElfFileSymbolFragments(ElfSymbolTableManager * aElfSymbolTableManager):
		iPath(""), 
		iSymbolTableOffset(0), iSymbolTableSize(0),
		iFirstGlobal(0),
		iStringTableOffset(0), iStringTableSize(0),
		iElfSymbolTableManager(aElfSymbolTableManager)
		{
		iSectionNumberMap.clear();
		iSectionVaddrAddendMap.clear();
		}
	
	ElfFileSymbolFragments & operator=(const ElfFileSymbolFragments & aElfFileSymbolFragments){
		iPath = aElfFileSymbolFragments.iPath;
		iSymbolTableOffset = aElfFileSymbolFragments.iSymbolTableOffset;
		iSymbolTableSize = aElfFileSymbolFragments.iSymbolTableSize;
		iFirstGlobal = aElfFileSymbolFragments.iFirstGlobal;
		iStringTableOffset = aElfFileSymbolFragments.iStringTableOffset;
		iStringTableSize = aElfFileSymbolFragments.iStringTableSize;
		iSectionNumberMap = aElfFileSymbolFragments.iSectionNumberMap;
		iSectionVaddrAddendMap = aElfFileSymbolFragments.iSectionVaddrAddendMap;
		iElfSymbolTableManager = aElfFileSymbolFragments.iElfSymbolTableManager;
		return *this;
	}
	
	ElfFileSymbolFragments(const ElfFileSymbolFragments & aElfFileSymbolFragments){
		*this = aElfFileSymbolFragments;
	}
	
	

	void AddSymbolTable(String & aPath, size_t aOffset, size_t aSize, size_t aFirstGlobal);
	
	void AddStringTable(String & aPath, size_t aOffset, size_t aSize);
	String & GetPath() { return iPath; }
	size_t GetSymbolTableOffset() { return iSymbolTableOffset; }
	size_t GetSymbolTableSize() { return iSymbolTableSize; }
	size_t GetFirstGlobal() { return iFirstGlobal; }
	size_t GetStringTableOffset() { return iStringTableOffset; }
	size_t GetStringTableSize() { return iStringTableSize; }
	
	void SetSectionNumberMap(SectionNumberMap & aSectionNumberMap){
		iSectionNumberMap = aSectionNumberMap;
	}
	
	size_t LookupSection(size_t ndx);
	
	void SetSectionVaddrAddendMap(SectionVaddrAddendMap & aSectionVaddrAddendMap){
		iSectionVaddrAddendMap = aSectionVaddrAddendMap;
	}
	
	int LookupVaddrAddend(size_t ndx);
	
	void Validate() {
		assert(iPath.size() != 0);
		assert(iSymbolTableOffset != 0);
		assert(iSymbolTableSize != 0);
		assert(iStringTableOffset != 0);
		assert(iStringTableSize != 0);
		assert(!iSectionNumberMap.empty());
		assert(!iSectionVaddrAddendMap.empty());
	}
	
	void Reset(){
		new (this) ElfFileSymbolFragments(iElfSymbolTableManager);
	}
	
private:
	String iPath;
	size_t iSymbolTableOffset;
	size_t iSymbolTableSize;
	size_t iFirstGlobal;
	size_t iStringTableOffset;
	size_t iStringTableSize;
	SectionNumberMap iSectionNumberMap;
	SectionVaddrAddendMap iSectionVaddrAddendMap;
	ElfSymbolTableManager * iElfSymbolTableManager;
	
};

class ElfSymTabStringTable : public ElfStringTable {
public:
	ElfSymTabStringTable(ElfSymbolTableManager & aElfSymbolTableManager):
		iElfSymbolTableManager(aElfSymbolTableManager)
		{}
	
	virtual size_t Size();
	
private:
	ElfSymbolTableManager & iElfSymbolTableManager;
};

class ElfSymbolTableManager : public FileFragmentOwner {
public:
	ElfSymbolTableManager(Elf32Header & aElf32Header, E32RomImage & aRomImage, 
					      OutputFile & aOutputFile, ElfSectionManager & aElfSectionManager) :
		iElf32Header(aElf32Header), iRomImage(aRomImage), 
		iOutputFile(aOutputFile), iElfSectionManager(aElfSectionManager),
		iData(NULL),
		iSymbolStringTableSizeValid(false),
		iSymbolStringTableSize(0),
		iCurrentFragment(this),
		iStringTable(*this)
		{}
	
	// The FileFragmentOwner protocol
	virtual void GetFileFragmentData(FileFragmentData & aFileFragmentData );
	virtual size_t Size();
	virtual void DeleteFileFragmentData();
	virtual void AddData(OutputFile & aOutputFile);
	
	void AddSymbolTable(String & aPath, size_t aOffset, size_t aSize, size_t aFirstGlobal){
		iCurrentFragment.AddSymbolTable(aPath, aOffset, aSize, aFirstGlobal);
	}
	void AddStringTable(String & aPath, size_t aOffset, size_t aSize){
		iCurrentFragment.AddStringTable(aPath, aOffset, aSize);

	}
	virtual void AddSymbolFragment(){
		iSymbolFragments.push_back(iCurrentFragment);
	}
	virtual void AddSymbolTable();
	
	size_t GetSymTabStringsSectionSize();

	virtual void Finalize(SectionNumberMap & aSectionNumberMap, SectionVaddrAddendMap & aSectionVaddrAddendMap);
	
private:
	// Don't want one of these to be copied
	ElfSymbolTableManager(const ElfSymbolTableManager & aElfSymbolTableManager);
	
	ElfSymbolTableManager & operator=(const ElfSymbolTableManager & aElfSymbolTableManager);
	
	size_t GetSymTabSectionSize();
	size_t GetFirstNonLocalIndex();

private:
	typedef std::vector<ElfFileSymbolFragments> SymbolFragmentList;
private:
	Elf32Header & iElf32Header;
	E32RomImage & iRomImage;
	OutputFile & iOutputFile;
	ElfSectionManager & iElfSectionManager;
	
	char * iData;
	bool iSymbolStringTableSizeValid;
	size_t iSymbolStringTableSize;
	ElfFileSymbolFragments iCurrentFragment;
	SymbolFragmentList iSymbolFragments;
	ElfSymTabStringTable iStringTable;
};

#endif /*ELFSYMBOLTABLEMANAGER_H_*/
