#pragma once

#include <stdint.h>
#include "tarray.h"
#include "textureid.h"
#include "textures.h"
#include "basics.h"
#include "texmanip.h"
#include "name.h"

class FxAddSub;
struct BuildInfo;
class FMultipatchTextureBuilder;
class FScanner;

// Texture manager
class FTextureManager
{
	void (*progressFunc)();
	friend class FxAddSub;	// needs access to do a bounds check on the texture ID.
public:
	FTextureManager ();
	~FTextureManager ();

private:
	int ResolveLocalizedTexture(int texnum);

	int ResolveTextureIndex(int texnum, bool animate) const
	{
		if ((unsigned)texnum >= Textures.Size()) return -1;
		if (animate) texnum = Translation[texnum];
		//if (localize && Textures[texnum].Flags & TEXFLAG_HASLOCALIZATION) texnum = ResolveLocalizedTexture(texnum);
		return texnum;
	}

	FGameTexture *InternalGetTexture(int texnum, bool animate) const
	{
		texnum = ResolveTextureIndex(texnum, animate);
		if (texnum == -1) return nullptr;
		return Textures[texnum].Texture;
	}

	FTextureID ResolveTextureIndex(FTextureID texid, bool animate) const
	{
		return FSetTextureID(ResolveTextureIndex(texid.GetIndex(), animate));
	}

public:
	// This only gets used in UI code so we do not need PALVERS handling.
	FGameTexture* GetGameTextureByName(const char *name, bool animate = false, int flags = 0)
	{
		FTextureID texnum = GetTextureID(name, ETextureType::MiscPatch, flags);
		return InternalGetTexture(texnum.GetIndex(), animate);
	}

	FGameTexture* GetGameTexture(FTextureID texnum, bool animate = false) const
	{
		return InternalGetTexture(texnum.GetIndex(), animate);
	}

	FGameTexture* GetPalettedTexture(FTextureID texnum, bool animate = false, bool allowsubstitute = true) const
	{
		auto texid = ResolveTextureIndex(texnum.GetIndex(), animate);
		if (texid == -1) return nullptr;
		if (allowsubstitute && Textures[texid].Paletted > 0) texid = Textures[texid].Paletted;
		return Textures[texid].Texture;
	}

	FGameTexture* GameByIndex(int i, bool animate = false) const
	{
		return InternalGetTexture(i, animate);
	}

	FGameTexture* FindGameTexture(const char* texname, ETextureType usetype = ETextureType::MiscPatch, BITFIELD flags = TEXMAN_TryAny);

	bool OkForLocalization(FTextureID texnum, const char *substitute, int locnum);

	void FlushAll();
	void Listaliases();
	FTextureID GetFrontSkyLayer(FTextureID);
	FTextureID GetRawTexture(FTextureID tex, bool dontlookup = false);
	void SetRawTexture(FTextureID texid) 
	{ 
		int texidx = texid.GetIndex();
		if ((unsigned)texidx < Textures.Size())
		{
			Textures[texidx].RawTexture = texidx;
		}
	}

	//==========================================================================
	//
	// link a texture with a given lump
	//
	//==========================================================================

	TMap<int, FGameTexture*> linkedMap;
	void SetLinkedTexture(int lump, FGameTexture* tex);
	FGameTexture* GetLinkedTexture(int lump);

	enum
	{
		TEXMAN_TryAny = 1,
		TEXMAN_Overridable = 2,
		TEXMAN_ReturnFirst = 4,
		TEXMAN_AllowSkins = 8,
		TEXMAN_ShortNameOnly = 16,
		TEXMAN_DontCreate = 32,
		TEXMAN_Localize = 64,
		TEXMAN_ForceLookup = 128,
		TEXMAN_NoAlias = 256,
		TEXMAN_ReturnAll = 512,
	};

	enum
	{
		HIT_Wall = 1,
		HIT_Flat = 2,
		HIT_Sky = 4,
		HIT_Sprite = 8,

		HIT_Columnmode = HIT_Wall|HIT_Sky|HIT_Sprite
	};

	FTextureID CheckForTexture (const char *name, ETextureType usetype, BITFIELD flags=TEXMAN_TryAny);
	FTextureID GetTextureID (const char *name, ETextureType usetype, BITFIELD flags=0);
	int ListTextures (const char *name, TArray<FTextureID> &list, bool listall = false);

	void AddGroup(int wadnum, int ns, ETextureType usetype);
	void AddPatches (int lumpnum);
	void AddHiresTextures (int wadnum);
	void LoadTextureDefs(int wadnum, const char *lumpname, FMultipatchTextureBuilder &build);
	void ParseColorization(FScanner& sc);
	void ParseTextureDef(int remapLump, FMultipatchTextureBuilder &build);
	void SortTexturesByType(int start, int end);
	bool AreTexturesCompatible (FTextureID picnum1, FTextureID picnum2);
	void AddLocalizedVariants();

	FTextureID CreateTexture (int lumpnum, ETextureType usetype=ETextureType::Any);	// Also calls AddTexture
	FTextureID AddGameTexture(FGameTexture* texture, bool addtohash = true);
	FTextureID GetDefaultTexture() const { return DefaultTexture; }

	void LoadTextureX(int wadnum, FMultipatchTextureBuilder &build);
	void AddTexturesForWad(int wadnum, FMultipatchTextureBuilder &build);
	void Init();
	void AddTextures(void (*progressFunc_)(), void (*checkForHacks)(BuildInfo&), void (*customtexturehandler)() = nullptr);
	void DeleteAll();

	void ReplaceTexture (FTextureID texid, FGameTexture *newtexture, bool free);

	int NumTextures () const { return (int)Textures.Size(); }

	int GuesstimateNumTextures ();

	TextureManipulation* GetTextureManipulation(FName name)
	{
		return tmanips.CheckKey(name);
	}
	void InsertTextureManipulation(FName cname, TextureManipulation tm)
	{
		tmanips.Insert(cname, tm);
	}
	void RemoveTextureManipulation(FName cname)
	{
		tmanips.Remove(cname);
	}

	void AddAlias(const char* name, int texindex);
	void AddAlias(const char* name, FTextureID texindex)
	{
		AddAlias(name, texindex.GetIndex());
	}

private:

	// texture counting
	int CountTexturesX ();
	int CountLumpTextures (int lumpnum);
	void AdjustSpriteOffsets();

	// Build tiles
	//int CountBuildTiles ();

public:

	TArray<uint8_t>& GetNewBuildTileData()
	{
		BuildTileData.Reserve(1);
		return BuildTileData.Last();
	}
	TArray<TArray<uint8_t>>& GetBuildTileDataStore()
	{
		return BuildTileData;
	}

	FGameTexture* GameTexture(FTextureID id) { return Textures[id.GetIndex()].Texture; }
	void SetTranslation(FTextureID fromtexnum, FTextureID totexnum);

private:

	void InitPalettedVersions();

	// Switches

	struct TextureDescriptor
	{
		FGameTexture* Texture;
		int Paletted;		// redirection to paletted variant
		int FrontSkyLayer;	// and front sky layer,
		int RawTexture;		
		int HashNext;
		uint64_t Flags;
	};

	enum : uint64_t
	{
		TEXFLAG_HASLOCALIZATION = 1,
	};
public:
	constexpr static int TEXFLAG_FIRSTUSER = 65536;	// this leaves 16 flags to the texture manager and 48 flags to the user
private:

	enum { HASH_END = -1, HASH_SIZE = 1027 };
	TArray<TextureDescriptor> Textures;
	TMap<uint64_t, int> LocalizedTextures;
	int HashFirst[HASH_SIZE];
	FTextureID DefaultTexture;
	TArray<int> FirstTextureForFile;
	TArray<TArray<uint8_t> > BuildTileData;
	TArray<int> Translation;

	TMap<FName, TextureManipulation> tmanips;
	TMap<FName, int> aliases;

public:

	short sintable[2048];	// for texture warping
	enum
	{
		SINMASK = 2047
	};

	FTextureID glPart2;
	FTextureID glPart;
	FTextureID mirrorTexture;
	bool usefullnames;

};

extern FTextureManager TexMan;