#pragma once

#include "TextureFormat.h"
#include "../Primitives/Vector2.h"

#include <memory>

#include <Containers/StringView.h>
#include <IO/Stream.h>

namespace nCine
{
	/**
		@brief Texture loader interface class
		
		Common base for all texture file loaders. Decodes a texture file into raw pixel data together with
		its dimensions, pixel format and MIP map chain, ready to be uploaded to the GPU by @ref Texture.
		Concrete subclasses handle individual file formats and are instantiated through the @ref createFromFile()
		factory according to the file extension.
	*/
	class ITextureLoader
	{
	public:
		virtual ~ITextureLoader() { }

		/**
		 * @brief Returns `true` if the texture has been correctly loaded
		 */
		inline bool hasLoaded() const {
			return _hasLoaded;
		}

		/**
		 * @brief Returns texture width in pixels
		 */
		inline std::int32_t width() const {
			return _width;
		}
		/**
		 * @brief Returns texture height in pixels
		 */
		inline std::int32_t height() const {
			return _height;
		}
		/**
		 * @brief Returns texture size as a `Vector2i`
		 */
		inline Vector2i size() const {
			return Vector2i(_width, _height);
		}
		/**
		 * @brief Returns the number of MIP maps stored in the texture file
		 */
		inline std::int32_t mipMapCount() const {
			return _mipMapCount;
		}
		/**
		 * @brief Returns texture data size in bytes
		 */
		inline std::uint32_t dataSize() const {
			return _dataSize;
		}
		/**
		 * @brief Returns the texture data size in bytes for the specified MIP map level
		 *
		 * @param mipMapLevel  Zero-based MIP map level
		 */
		std::int32_t dataSize(std::uint32_t mipMapLevel) const;
		/**
		 * @brief Returns the texture format object
		 */
		inline const TextureFormat& texFormat() const {
			return _texFormat;
		}
		/**
		 * @brief Returns the pointer to pixel data
		 */
		inline const std::uint8_t* pixels() const {
			return _pixels.get();
		}
		/**
		 * @brief Returns the pointer to pixel data for the specified MIP map level
		 *
		 * @param mipMapLevel  Zero-based MIP map level
		 */
		const std::uint8_t* pixels(std::uint32_t mipMapLevel) const;

		//static std::unique_ptr<ITextureLoader> createFromMemory(const unsigned char* bufferPtr, unsigned long int bufferSize);
		/**
		 * @brief Returns the proper texture loader according to the file extension
		 *
		 * @param filename  Path of the texture file to load
		 */
		static std::unique_ptr<ITextureLoader> createFromFile(const Death::Containers::StringView filename);
		/**
		 * @brief Returns the proper texture loader for a texture that is already open
		 *
		 * @param fileHandle  Stream the texture is read from
		 * @param path        Path the stream was opened from, only its extension is used
		 *
		 * For a texture that has no file of its own to open --- one inside a `.pak` container above all,
		 * where @ref createFromFile() has no path that would reach it.
		 */
		static std::unique_ptr<ITextureLoader> createFromStream(std::unique_ptr<Death::IO::Stream> fileHandle, const Death::Containers::StringView path);

	protected:
#ifndef DOXYGEN_GENERATING_OUTPUT
		/** @brief Whether the loading process has been successful */
		bool _hasLoaded;
		/** @brief Texture file handle */
		std::unique_ptr<Death::IO::Stream> _fileHandle;

		std::int32_t _width;
		std::int32_t _height;
		std::int32_t _headerSize;
		std::uint32_t _dataSize;
		std::int32_t _mipMapCount;
		std::unique_ptr<std::uint32_t[]> _mipDataOffsets;
		std::unique_ptr<std::uint32_t[]> _mipDataSizes;
		TextureFormat _texFormat;
		std::unique_ptr<std::uint8_t[]> _pixels;
#endif

		/** @brief An empty constructor only used by `TextureLoaderRaw` */
		ITextureLoader();
		explicit ITextureLoader(std::unique_ptr<Death::IO::Stream> fileHandle);

		static std::unique_ptr<ITextureLoader> createLoader(std::unique_ptr<Death::IO::Stream> fileHandle, const Death::Containers::StringView path);
		/**
		 * @brief Loads pixel data from a texture file holding either compressed or uncompressed data
		 *
		 * @param format  Backend-neutral pixel format describing the decoded pixels
		 */
		void loadPixels(PixelFormat format);
	};

#ifndef DOXYGEN_GENERATING_OUTPUT
	/** @brief A loader created when the texture file extension is not recognized */
	class InvalidTextureLoader : public ITextureLoader
	{
	public:
		explicit InvalidTextureLoader(std::unique_ptr<Death::IO::Stream> fileHandle)
			: ITextureLoader(std::move(fileHandle)) { }
	};
#endif
}
