#ifndef ROM_HH
#define ROM_HH

#include "MappedFile.hh"

#include "MemBuffer.hh"
#include "sha1.hh"
#include "static_string_view.hh"

#include <cassert>
#include <cstdint>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <string_view>

namespace openmsx {

class MSXMotherBoard;
class XMLElement;
class DeviceConfig;
class FileContext;
class RomDebuggable;
class TclObject;

class Rom final
{
public:
	Rom(std::string name, static_string_view description,
	    DeviceConfig& config, std::string_view id = {});
	Rom(Rom&& other) noexcept;
	~Rom();

	[[nodiscard]] const uint8_t& operator[](size_t address) const {
		assert(address < rom.size());
		return rom[address];
	}
	[[nodiscard]] auto size()  const { return rom.size(); }
	[[nodiscard]] auto begin() const { return rom.begin(); }
	[[nodiscard]] auto end()   const { return rom.end(); }

	[[nodiscard]] const std::string& getFilename() const { return filename; }
	[[nodiscard]] const std::string& getName() const { return name; }
	[[nodiscard]] std::string_view getDescription() const { return description; }
	[[nodiscard]] const Sha1Sum& getOriginalSHA1() const;
	[[nodiscard]] const Sha1Sum& getSHA1() const;

	void addPadding(size_t newSize, uint8_t filler = 0xff);

	/**
	 * Add dict values with info to result
	 */
	void getInfo(TclObject& result) const;

private:
	void init(MSXMotherBoard& motherBoard, XMLElement& config,
	          const FileContext& context);
	[[nodiscard]] bool checkSHA1(const XMLElement& config) const;

private:
	// !! update the move constructor when changing these members !!
	std::span<const uint8_t> rom;
	MemBuffer<uint8_t> extendedRom;

	std::string filename;
	std::optional<MappedFile<uint8_t>> mmap; // non-const to allow patching

	mutable Sha1Sum originalSha1;
	mutable Sha1Sum actualSha1;
	std::string name;
	/*const*/ static_string_view description; // not const to allow move

	// This must come after 'name':
	//   the destructor of RomDebuggable calls Rom::getName(), which still
	//   needs the Rom::name member.
	std::unique_ptr<RomDebuggable> romDebuggable; // can be nullptr
};

} // namespace openmsx

#endif
