/**
 * @file include/retdec/loader/loader/image.h
 * @brief Declaration of loadable image class.
 * @copyright (c) 2017 Avast Software, licensed under the MIT license
 */

#ifndef RETDEC_LOADER_RETDEC_LOADER_IMAGE_H
#define RETDEC_LOADER_RETDEC_LOADER_IMAGE_H

#include <memory>

#include "retdec/utils/byte_value_storage.h"
#include "retdec/fileformat/fftypes.h"
#include "retdec/fileformat/file_format/file_format.h"
#include "retdec/loader/loader/segment.h"
#include "retdec/loader/utils/name_generator.h"

namespace retdec {
namespace loader {

class Image : public retdec::utils::ByteValueStorage
{
public:
	Image(const std::shared_ptr<retdec::fileformat::FileFormat>& fileFormat);

	/**
	 * Virtual method that should be overriden in every subclass of Image. Performs the logic of loading.
	 *
	 * @return True if loading was successful, otherwise false.
	 */
	virtual bool load() = 0;

	virtual retdec::utils::Endianness getEndianness() const override;
	virtual std::size_t getNibbleLength() const override;
	virtual std::size_t getByteLength() const override;
	virtual std::size_t getWordLength() const override;
	virtual std::size_t getBytesPerWord() const override;
	virtual std::size_t getNumberOfNibblesInByte() const override;
	virtual bool hasMixedEndianForDouble() const override;

	virtual bool getXByte(std::uint64_t address, std::uint64_t x, std::uint64_t& res, retdec::utils::Endianness e = retdec::utils::Endianness::UNKNOWN) const override;
	virtual bool getXBytes(std::uint64_t address, std::uint64_t x, std::vector<std::uint8_t>& res) const override;

	virtual bool setXByte(std::uint64_t address, std::uint64_t x, std::uint64_t val, retdec::utils::Endianness e = retdec::utils::Endianness::UNKNOWN) override;
	virtual bool setXBytes(std::uint64_t address, const std::vector<std::uint8_t>& res) override;

	retdec::fileformat::FileFormat* getFileFormat();
	const retdec::fileformat::FileFormat* getFileFormat() const;
	std::weak_ptr<retdec::fileformat::FileFormat> getFileFormatWptr() const;

	std::size_t getNumberOfSegments() const;
	const std::vector<std::unique_ptr<Segment>>& getSegments() const;

	std::uint64_t getBaseAddress() const;
	void setBaseAddress(std::uint64_t baseAddress);

	bool hasDataOnAddress(std::uint64_t address) const;
	bool hasDataInitializedOnAddress(std::uint64_t address) const;
	bool hasReadOnlyDataOnAddress(std::uint64_t address) const;
	bool hasSegmentOnAddress(std::uint64_t address) const;
	bool isPointer(std::uint64_t address, std::uint64_t* pointer = nullptr) const;

	Segment* getSegment(std::size_t index);
	Segment* getSegment(const std::string& name);
	Segment* getSegmentWithIndex(std::size_t index);
	Segment* getSegmentFromAddress(std::uint64_t address);
	const Segment* getSegment(std::size_t index) const;
	const Segment* getSegment(const std::string& name) const;
	const Segment* getSegmentWithIndex(std::size_t index) const;
	const Segment* getSegmentFromAddress(std::uint64_t address) const;
	const Segment* getEpSegment();

	std::pair<const std::uint8_t*, std::uint64_t> getRawSegmentData(std::uint64_t address) const;

	const std::string& getStatusMessage() const;
	const retdec::fileformat::LoaderErrorInfo & getLoaderErrorInfo() const;

protected:
	Segment* insertSegment(std::unique_ptr<Segment> segment);
	void removeSegment(Segment* segment);
	void nameSegment(Segment* segment);
	void sortSegments();

	void setStatusMessage(const std::string& message);

private:
	const Segment* _getSegment(std::size_t index) const;
	const Segment* _getSegment(const std::string& name) const;
	const Segment* _getSegmentWithIndex(std::size_t index) const;
	const Segment* _getSegmentFromAddress(std::uint64_t address) const;

	std::shared_ptr<retdec::fileformat::FileFormat> _fileFormat;
	std::vector<std::unique_ptr<Segment>> _segments;
	std::uint64_t _baseAddress;
	NameGenerator _namelessSegNameGen;
	std::string _statusMessage;
};

} // namespace loader
} // namespace retdec

#endif
