/**
* @file include/retdec/bin2llvmir/providers/names.h
* @brief Database of objects' names in binary.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/

#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_NAMES_H
#define RETDEC_BIN2LLVMIR_PROVIDERS_NAMES_H

#include <map>
#include <set>

#include "retdec/bin2llvmir/providers/config.h"
#include "retdec/bin2llvmir/providers/debugformat.h"
#include "retdec/bin2llvmir/providers/demangler.h"
#include "retdec/bin2llvmir/providers/fileimage.h"
#include "retdec/bin2llvmir/providers/lti.h"
#include "retdec/common/address.h"

namespace retdec {
namespace bin2llvmir {

namespace names {

const std::string entryPointName               = "entry_point";
const std::string generatedImportPrefix        = "imported_function_ord_";
const std::string generatedFunctionPrefix      = "function_";
const std::string generatedFunctionPrefixIDA   = "ida_";
const std::string generatedFunctionPrefixUnk   = "unknown_";
const std::string generatedGlobalVarPrefix     = "global_var_";
const std::string generatedStackVarPrefix      = "stack_var_";
const std::string generatedTempVarPrefix       = "v";
const std::string generatedBasicBlockPrefix    = "dec_label_pc_";
const std::string generatedUndefFunctionPrefix = "__decompiler_undefined_function_";
const std::string generatedVtablePrefix        = "vtable_";
const std::string asm2llvmGv                   = "_asm_program_counter";
const std::string pseudoCallFunction           = "__pseudo_call";
const std::string pseudoReturnFunction         = "__pseudo_return";
const std::string pseudoBranchFunction         = "__pseudo_branch";
const std::string pseudoCondBranchFunction     = "__pseudo_cond_branch";
const std::string pseudoX87dataLoadFunction    = "__frontend_reg_load.fpr";
const std::string pseudoX87dataStoreFunction   = "__frontend_reg_store.fpr";

std::string generateFunctionName(common::Address a, bool ida = false);
std::string generateFunctionNameUnknown(common::Address a, bool ida = false);
std::string generateGlobalVarName(
		common::Address a,
		const std::string& name = std::string());
std::string generateStackVarName(
		int offset,
		const std::string& name = std::string());
std::string generateBasicBlockName(common::Address a);
std::string generateTempVariableName(common::Address a, unsigned cntr);
std::string generateFunctionNameUndef(unsigned cntr);
std::string generateVtableName(common::Address a);

} // namespace names

/**
 * Representation of one name.
 */
class Name
{
	public:
		/**
		 * Name type and its priority.
		 * Lower number -> higher priority.
		 */
		enum class eType
		{
			// This is not set automatically in this module -> for manual use
			// only - when some bin2llvmir part wants to really make sure
			// a certain name is used.
			HIGHEST_PRIORITY = 0,
			// Stuff from config.
			CONFIG_FUNCTION,
			CONFIG_GLOBAL,
			CONFIG_SEGMENT,
			//
			DEBUG_FUNCTION,
			DEBUG_GLOBAL,
			//
			LTI_FUNCTION,
			// Stuff from file image.
			IMPORT,
			EXPORT,
			SYMBOL_FUNCTION,
			SYMBOL_OBJECT,
			SYMBOL_FILE,
			SYMBOL_OTHER,
			STATIC_CODE,
			ENTRY_POINT,
			SECTION,
			IMPORT_GENERATED,
			// This is not set automatically in this module -> for manual use
			// only - when some bin2llvmir part wants to use some name if
			// nothing better is available.
			LOWEST_PRIORITY,
			// Invalid - somethingwent wrong, do not use this entry.
			INVALID,
		};

	public:
		Name();
		Name(Config* c, const std::string& name, eType type, Lti* lti = nullptr);

		operator std::string() const;
		explicit operator bool() const;
		bool operator<(const Name& o) const;

		const std::string& getName() const;
		eType getType() const;

	private:
		void fixPic32Mangling();
		void fixPostfix();

	private:
		std::string _name;
		eType _type = eType::INVALID;
};

/**
 * Representation of all the names for one object.
 */
class Names
{
	public:
		using iterator = typename std::set<Name>::iterator;

	public:
		bool addName(
				Config* c,
				const std::string& name,
				Name::eType type,
				Lti* lti = nullptr);

		const Name& getPreferredName();

		iterator begin();
		iterator end();
		std::size_t size() const;
		bool empty() const;

	private:
		std::set<Name> _names;
		static Name _emptyName;
};

/**
 * Names container.
 */
class NameContainer
{
	public:
		NameContainer(
				llvm::Module* m,
				Config* c,
				DebugFormat* d,
				FileImage* i,
				Demangler* dm,
				Lti* lti = nullptr);

		bool addNameForAddress(
				retdec::common::Address a,
				const std::string& name,
				Name::eType type,
				Lti* lti = nullptr);

		const Names& getNamesForAddress(retdec::common::Address a);
		const Name& getPreferredNameForAddress(retdec::common::Address a);

	private:
		void initFromConfig();
		void initFromDebug();
		void initFromImage();

		std::string getNameFromImportLibAndOrd(
				const std::string& libName,
				int ord);
		bool loadImportOrds(const std::string& libName);

	private:
		/// <ordinal number, function name>
		using ImportOrdMap = std::map<int, std::string>;

	private:
		Config* _config = nullptr;
		DebugFormat* _debug = nullptr;
		FileImage* _image = nullptr;
		Lti* _lti = nullptr;

		std::map<retdec::common::Address, Names> _data;
		/// <library name without suffix ".dll", map with ordinals>
		std::map<std::string, ImportOrdMap> _dllOrds;
};

/**
 * Names provider.
 */
class NamesProvider
{
	public:
		static NameContainer* addNames(
				llvm::Module* m,
				Config* c,
				DebugFormat* d,
				FileImage* i,
				Demangler* dm,
				Lti* lti);
		static NameContainer* getNames(llvm::Module* m);
		static bool getNames(llvm::Module* m, NameContainer*& names);
		static void clear();

	private:
		static std::map<llvm::Module*, NameContainer> _module2names;
};

} // namespace bin2llvmir
} // namespace retdec

#endif
