#pragma once

#include "ehs/Types.h"
#include "ehs/BaseObj.h"
#include "ehs/Str.h"
#include "ImgCodec.h"

namespace ehs
{
	enum class Resampling : UInt_8
	{
		NONE,
		NEAREST_NEIGHBOR
	};

	class EHS_LIB_IO Img : public BaseObj
	{
	private:
		static Array<ImgCodec> codecs;

	protected:
		UInt_64 hashId;
		Str_8 id;
		UInt_8 byteDepth;
		UInt_8 channels;
		Vec2_u64 resolution;
		UInt_64 size;
		Byte* data;

	public:
		static bool HasCodec(UInt_64 hashExt);

		static bool HasCodec(const Str_8& ext);

		static bool AddCodec(ImgCodec codec);

		static const ImgCodec* GetCodec(UInt_64 hashExt);

		static const ImgCodec* GetCodec(const Str_8& ext);

		~Img() override;

		Img();

		Img(const Str_8& filePath);

		Img(Str_8 id, UInt_8 byteDepth, UInt_8 channels, const Vec2_u64& resolution, const Byte* data);

		Img(Str_8 id, UInt_8 byteDepth, UInt_8 channels, const Vec2_u64& resolution);

		Img(Img&& img) noexcept;

		Img(const Img& img);

		Img& operator=(Img&& img) noexcept;

		Img& operator=(const Img& img);

		operator const Byte* () const;

		operator Byte* ();

		void Release();

		UInt_64 GetHashId() const;

		void SetId(Str_8 newId);

		Str_8 GetId() const;

		UInt_8 GetByteDepth() const;

		UInt_8 GetBitDepth() const;

		UInt_8 GetChannels() const;

		Vec2_u64 GetResolution() const;

		UInt_64 GetSize() const;

		void SetPixel(UInt_64 index, const Byte* pixel);

		void GetPixel(UInt_64 index, Byte* pixel) const;

		void SetPixel(UInt_64 x, UInt_64 y, const Byte* pixel);

		void GetPixel(UInt_64 x, UInt_64 y, Byte* pixel) const;

		void Resize(Resampling method, const Vec2_u64& newResolution);

		Img GetResized(Resampling method, const Vec2_u64& newResolution) const;

		void ToRGBA();

		Img GetAsRGBA() const;

		void ToRGB();

		Img GetAsRGB() const;

		void ToMonoA();

		Img GetAsMonoA() const;

		void ToMono();

		Img GetAsMono() const;

		void To32();

		Img GetAs32() const;

		void To24();

		Img GetAs24() const;

		void To16();

		Img GetAs16() const;

		void To8();

		Img GetAs8() const;

		bool IsValid() const;

		bool Export(const Str_8& filePath) const;

	private:
		Img GetNearestNeighbor(const Vec2_u64& newResolution) const;

		void NearestNeighbor(const Vec2_u64& newResolution);

		void RGB_To_RGBA(UInt_64 newSize, Byte* buffer) const;

		void MonoA_To_RGBA(UInt_64 newSize, Byte* buffer) const;

		void Mono_To_RGBA(UInt_64 newSize, Byte* buffer) const;

		void RGBA_To_RGB(UInt_64 newSize, Byte* buffer) const;

		void MonoA_To_RGB(UInt_64 newSize, Byte* buffer) const;

		void Mono_To_RGB(UInt_64 newSize, Byte* buffer) const;

		void RGBA_To_MonoA(UInt_64 newSize, Byte* buffer) const;

		void RGB_To_MonoA(UInt_64 newSize, Byte* buffer) const;

		void Mono_To_MonoA(UInt_64 newSize, Byte* buffer) const;

		void RGBA_To_Mono(UInt_64 newSize, Byte* buffer) const;

		void RGB_To_Mono(UInt_64 newSize, Byte* buffer) const;

		void MonoA_To_Mono(UInt_64 newSize, Byte* buffer) const;

		void BD24_to_BD32(UInt_64 newSize, Byte* buffer) const;

		void BD16_to_BD32(UInt_64 newSize, Byte* buffer) const;

		void BD8_to_BD32(UInt_64 newSize, Byte* buffer) const;

		void BD32_to_BD24(UInt_64 newSize, Byte* buffer) const;

		void BD16_to_BD24(UInt_64 newSize, Byte* buffer) const;

		void BD8_to_BD24(UInt_64 newSize, Byte* buffer) const;

		void BD32_to_BD16(UInt_64 newSize, Byte* buffer) const;

		void BD24_to_BD16(UInt_64 newSize, Byte* buffer) const;

		void BD8_to_BD16(UInt_64 newSize, Byte* buffer) const;

		void BD32_to_BD8(UInt_64 newSize, Byte* buffer) const;

		void BD24_to_BD8(UInt_64 newSize, Byte* buffer) const;

		void BD16_to_BD8(UInt_64 newSize, Byte* buffer) const;
	};

	bool EncodeQOI(const ehs::ImgCodec* codec, ehs::Serializer<ehs::UInt_64>& out, const ehs::Img* in);

	bool DecodeQOI(const ehs::ImgCodec* codec, ehs::Serializer<ehs::UInt_64>& in, ehs::Img* out);

	bool DecodePNG(const ehs::ImgCodec* codec, ehs::Serializer<ehs::UInt_64>& in, ehs::Img* out);
}