/**
 * @file src/fileinfo/file_presentation/config_presentation.cpp
 * @brief Config DB presentation class.
 * @copyright (c) 2017 Avast Software, licensed under the MIT license
 */

#include "retdec/utils/conversion.h"
#include "retdec/utils/string.h"
#include "fileinfo/file_presentation/config_presentation.h"
#include "fileinfo/file_presentation/getters/pattern_config_getter/pattern_config_getter.h"

using namespace retdec::config;
using namespace retdec::utils;
using namespace retdec::cpdetect;
using namespace retdec::fileformat;

namespace retdec {
namespace fileinfo {

/**
 * Constructor
 * @param fileinfo_ Information about file
 * @param file_ Name of configuration file
 */
ConfigPresentation::ConfigPresentation(FileInformation &fileinfo_, std::string file_) :
	FilePresentation(fileinfo_), configFile(file_), stateIsValid(true)
{
	try
	{
		outDoc.readJsonFile(configFile);
	}
	catch (const FileNotFoundException&)
	{
		// file does not exist, it will be created at the end.
	}
	catch (const ParseException &e)
	{
		stateIsValid = false;
		errorMessage = e.what();
	}
}

/**
 * Destructor
 */
ConfigPresentation::~ConfigPresentation()
{
	outDoc.generateJsonFile(configFile);
}

/**
 * Present information about used compiler or packer
 * Method presents only first detected tool
 */
void ConfigPresentation::presentCompiler()
{
	const std::size_t noOfCompilers = fileinfo.getNumberOfDetectedCompilers();
	if(!noOfCompilers)
	{
		return;
	}

	bool similarityFlag = false, actualSimilarity;
	double percentage = 0.0;
	bool heuristics;
	outDoc.tools.clear();

	for(std::size_t i = 0; i < noOfCompilers; ++i)
	{
		const auto& detectedTool = fileinfo.toolInfo.detectedTools[i];

		if(detectedTool.source == DetectionMethod::SIGNATURE)
		{
			heuristics = false;
			actualSimilarity = (detectedTool.agreeCount != detectedTool.impCount);
			if(actualSimilarity)
			{
				if(similarityFlag)
				{
					continue;
				}
				similarityFlag = true;
			}
		}
		else
		{
			heuristics = true;
		}

		retdec::common::ToolInfo ci;
		ci.setName(toLower(detectedTool.name));
		ci.setType(toLower(toolTypeToString(detectedTool.type)));
		ci.setVersion(toLower(detectedTool.versionInfo));
		ci.setAdditionalInfo(detectedTool.additionalInfo);

		if(detectedTool.impCount)
		{
			percentage = static_cast<double>(detectedTool.agreeCount) / detectedTool.impCount * 100;
		}
		else
		{
			percentage = 0.0;
		}
		ci.setPercentage(percentage);
		ci.setIdenticalSignificantNibbles(detectedTool.agreeCount);
		ci.setTotalSignificantNibbles(detectedTool.impCount);
		ci.setIsFromHeuristics(heuristics);

		outDoc.tools.push_back(ci);
	}
}

/**
 * Present information about original programming language(s)
 */
void ConfigPresentation::presentLanguages()
{
	const auto noOfLanguages = fileinfo.toolInfo.detectedLanguages.size();
	if(!noOfLanguages)
	{
		return;
	}
	outDoc.languages.clear();

	for(std::size_t i = 0; i < noOfLanguages; ++i)
	{
		retdec::common::Language l(fileinfo.toolInfo.detectedLanguages[i].name);
		l.setIsBytecode(fileinfo.toolInfo.detectedLanguages[i].bytecode);

		outDoc.languages.insert(l);
	}
}

/**
 * Present information about detected patterns
 */
void ConfigPresentation::presentPatterns()
{
	PatternConfigGetter(fileinfo, &outDoc);
}

bool ConfigPresentation::present()
{
	if(!stateIsValid)
	{
		return false;
	}
	else if(returnCode == ReturnCode::FILE_PROBLEM || returnCode == ReturnCode::UNKNOWN_FORMAT || returnCode == ReturnCode::FILE_NOT_EXIST)
	{
		return true;
	}

	outDoc.parameters.setInputFile(fileinfo.getPathToFile());

	if(fileinfo.getFileFormatEnum() == Format::ELF)
	{
		outDoc.fileFormat.setIsElf();
	}
	else if(fileinfo.getFileFormatEnum() == Format::PE)
	{
		outDoc.fileFormat.setIsPe();
	}
	else if(fileinfo.getFileFormatEnum() == Format::COFF)
	{
		outDoc.fileFormat.setIsCoff();
	}
	else if(fileinfo.getFileFormatEnum() == Format::MACHO)
	{
		outDoc.fileFormat.setIsMacho();
	}
	else if(fileinfo.getFileFormatEnum() == Format::INTEL_HEX)
	{
		outDoc.fileFormat.setIsIntelHex();
	}
	else if(fileinfo.getFileFormatEnum() == Format::RAW_DATA)
	{
		outDoc.fileFormat.setIsRaw();
	}
	else
	{
		outDoc.fileFormat.setIsUnknown();
	}

	const auto ft = fileinfo.getFileType();
	if(ft == "Executable file")
	{
		outDoc.fileType.setIsExecutable();
	}
	else if(ft == "DLL")
	{
		outDoc.fileType.setIsShared();
	}
	else if(ft == "Relocatable file")
	{
		outDoc.fileType.setIsObject();
	}
	else
	{
		outDoc.fileType.setIsUnknown();
	}

	const auto fc = fileinfo.getFileClass();
	if(fc == "32-bit")
	{
		outDoc.fileFormat.setIs32bit();
	}
	else if(fc == "64-bit")
	{
		outDoc.fileFormat.setIs64bit();
	}

	outDoc.architecture.setName(toLower(fileinfo.getTargetArchitecture()));
	unsigned long long bitsInWord;
	if(strToNum(fileinfo.getNumberOfBitsInWordStr(), bitsInWord))
	{
		outDoc.architecture.setBitSize(bitsInWord);
	}

	const auto fe = fileinfo.getEndianness();
	if(fe == "Little endian")
	{
		outDoc.architecture.setIsEndianLittle();
	}
	else if(fe == "Big endian")
	{
		outDoc.architecture.setIsEndianBig();
	}
	else
	{
		outDoc.architecture.setIsEndianUnknown();
	}

	if(fileinfo.toolInfo.entryPointAddress)
	{
		outDoc.parameters.setEntryPoint(fileinfo.toolInfo.epAddress);
	}

	presentCompiler();
	presentLanguages();
	presentPatterns();
	return true;
}

/**
 * Get error message
 * @return Error message or empty string if presentation went OK
 */
std::string ConfigPresentation::getErrorMessage() const
{
	return errorMessage;
}

} // namespace fileinfo
} // namespace retdec
