/*
** textures.h
**
**---------------------------------------------------------------------------
** Copyright 2005-2016 Randy Heit
** Copyright 2005-2019 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
**    derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/

#ifndef __TEXTURES_H
#define __TEXTURES_H

#include "basics.h"
#include "vectors.h"
#include "colormatcher.h"
#include "renderstyle.h"
#include "textureid.h"
#include <vector>
#include "hw_texcontainer.h"
#include "floatrect.h"
#include "refcounted.h"

class FImageSource;
class FGameTexture;
class IHardwareTexture;


enum
{
	CLAMP_NONE = 0,
	CLAMP_X,
	CLAMP_Y,
	CLAMP_XY,
	CLAMP_XY_NOMIP,
	CLAMP_NOFILTER,
	CLAMP_NOFILTER_X,
	CLAMP_NOFILTER_Y,
	CLAMP_NOFILTER_XY,
	CLAMP_CAMTEX,
	NUMSAMPLERS
};

enum MaterialShaderIndex
{
	SHADER_Default,
	SHADER_Warp1,
	SHADER_Warp2,
	SHADER_Specular,
	SHADER_PBR,
	SHADER_Paletted,
	SHADER_NoTexture,
	SHADER_BasicFuzz,
	SHADER_SmoothFuzz,
	SHADER_SwirlyFuzz,
	SHADER_TranslucentFuzz,
	SHADER_JaggedFuzz,
	SHADER_NoiseFuzz,
	SHADER_SmoothNoiseFuzz,
	SHADER_SoftwareFuzz,
	FIRST_USER_SHADER
};

enum texflags
{
	// These get Or'ed into uTextureMode because it only uses its 3 lowermost bits.
	TEXF_Brightmap = 0x10000,
	TEXF_Detailmap = 0x20000,
	TEXF_Glowmap = 0x40000,
	TEXF_ClampY = 0x80000,
};



enum
{
	SFlag_Brightmap = 1,
	SFlag_Detailmap = 2,
	SFlag_Glowmap = 4,
};

struct UserShaderDesc
{
	FString shader;
	MaterialShaderIndex shaderType;
	FString defines;
	bool disablealphatest = false;
	uint8_t shaderFlags = 0;
};

extern TArray<UserShaderDesc> usershaders;

class FBitmap;
struct FRemapTable;
struct FCopyInfo;
class FScanner;

// Texture IDs
class FTextureManager;
class FTerrainTypeArray;
class IHardwareTexture;
class FMaterial;
class FMultipatchTextureBuilder;

extern int r_spriteadjustSW, r_spriteadjustHW;

enum FTextureFormat : uint32_t
{
	TEX_Pal,
	TEX_Gray,
	TEX_RGB,		// Actually ARGB

	TEX_Count
};

class ISoftwareTexture
{
public:
	virtual ~ISoftwareTexture() = default;
};

class FGLRenderState;

struct spriteframewithrotate;
class FSerializer;
namespace OpenGLRenderer
{
	class FGLRenderState;
	class FHardwareTexture;
}

union FContentIdBuilder
{
	uint64_t id;
	struct
	{
		unsigned imageID : 24;
		unsigned translation : 16;
		unsigned expand : 1;
		unsigned scaler : 4;
		unsigned scalefactor : 4;
	};
};

struct FTextureBuffer
{
	uint8_t *mBuffer = nullptr;
	bool mFreeBuffer = true;
	int mWidth = 0;
	int mHeight = 0;
	uint64_t mContentId = 0;	// unique content identifier. (Two images created from the same image source with the same settings will return the same value.)

	FTextureBuffer() = default;

	~FTextureBuffer()
	{
		if (mBuffer && mFreeBuffer) delete[] mBuffer;
	}

	FTextureBuffer(const FTextureBuffer &other) = delete;
	FTextureBuffer(FTextureBuffer &&other) noexcept
	{
		mBuffer = other.mBuffer;
		mWidth = other.mWidth;
		mHeight = other.mHeight;
		mContentId = other.mContentId;
		mFreeBuffer = other.mFreeBuffer;
		other.mBuffer = nullptr;
	}

	FTextureBuffer& operator=(FTextureBuffer &&other) noexcept
	{
		mBuffer = other.mBuffer;
		mWidth = other.mWidth;
		mHeight = other.mHeight;
		mContentId = other.mContentId;
		mFreeBuffer = other.mFreeBuffer;
		other.mBuffer = nullptr;
		return *this;
	}

};

// Base texture class
class FTexture : public RefCountedBase
{
	friend class FGameTexture;	// only for the porting work

public:
	FHardwareTextureContainer SystemTextures;
protected:
	FloatRect* areas = nullptr;
	int SourceLump;
	uint16_t Width = 0, Height = 0;

	bool Masked = false;			// Texture (might) have holes
	bool bHasCanvas = false;
	bool bHdr = false; 				// only canvas textures for now.
	int8_t bTranslucent = -1;
	int8_t areacount = 0;			// this is capped at 4 sections.


public:

	IHardwareTexture* GetHardwareTexture(int translation, int scaleflags);
	virtual FImageSource *GetImage() const { return nullptr; }
	void CreateUpsampledTextureBuffer(FTextureBuffer &texbuffer, bool hasAlpha, bool checkonly);

	void CleanHardwareTextures()
	{
		SystemTextures.Clean();
	}

	void CleanPrecacheMarker()
	{
		SystemTextures.UnmarkAll();
	}

	void MarkForPrecache(int translation, int scaleflags)
	{
		SystemTextures.MarkForPrecache(translation, scaleflags);
	}

	void CleanUnused()
	{
		SystemTextures.CleanUnused();
	}

	int GetWidth() { return Width; }
	int GetHeight() { return Height; }

	bool isHardwareCanvas() const { return bHasCanvas; }	// There's two here so that this can deal with software canvases in the hardware renderer later.
	bool isCanvas() const { return bHasCanvas; }

	bool IsHDR() const { return bHdr; }

	int GetSourceLump() { return SourceLump; }	// needed by the scripted GetName method.
	void SetSourceLump(int sl) { SourceLump  = sl; }
	bool FindHoles(const unsigned char * buffer, int w, int h);

	void CopySize(FTexture* BaseTexture)
	{
		Width = BaseTexture->GetWidth();
		Height = BaseTexture->GetHeight();
	}

	// This is only used for the null texture and for Heretic's skies.
	void SetSize(int w, int h)
	{
		Width = w;
		Height = h;
	}

	bool TrimBorders(uint16_t* rect);
	int GetAreas(FloatRect** pAreas) const;

	// Returns the whole texture, stored in column-major order
	virtual TArray<uint8_t> Get8BitPixels(bool alphatex);
	virtual FBitmap GetBgraBitmap(const PalEntry *remap, int *trans = nullptr);

	static bool SmoothEdges(unsigned char * buffer,int w, int h);


	virtual void ResolvePatches() {}


	FTexture (int lumpnum = -1);

public:
	FTextureBuffer CreateTexBuffer(int translation, int flags = 0);
	virtual bool DetermineTranslucency();
	bool GetTranslucency()
	{
		return bTranslucent != -1 ? bTranslucent : DetermineTranslucency();
	}

public:

	void CheckTrans(unsigned char * buffer, int size, int trans);
	bool ProcessData(unsigned char * buffer, int w, int h, bool ispatch);
	int CheckRealHeight();

	friend class FTextureManager;
};

class FCanvas;
extern TArray<FCanvas*> AllCanvases;

// A texture that can be drawn to.

class FCanvasTexture : public FTexture
{
public:
	FCanvasTexture(int width, int height)
	{
		Width = width;
		Height = height;

		bHasCanvas = true;
		aspectRatio = (float)width / height;
	}

	~FCanvasTexture()
	{
		if (Canvas)
		{
			AllCanvases.Delete(AllCanvases.Find(Canvas));
			Canvas = nullptr;
		}
	}

	void NeedUpdate() { bNeedsUpdate = true; }
	void SetUpdated(bool rendertype) { bNeedsUpdate = false; bFirstUpdate = false; bLastUpdateType = rendertype; }
	bool CheckNeedsUpdate() const { return bNeedsUpdate; }

	void SetAspectRatio(double aspectScale, bool useTextureRatio) { aspectRatio = (float)aspectScale * (useTextureRatio? ((float)Width / Height) : 1); }

	FCanvas* Canvas = nullptr;

protected:

	bool bLastUpdateType = false;
	bool bNeedsUpdate = true;
public:
	bool bFirstUpdate = true;
	float aspectRatio;

	friend struct FCanvasTextureInfo;
	friend class FTextureAnimator;

private:
	void SetHDR(bool hdr) {
		bHdr = hdr;
	}
};


// A wrapper around a hardware texture, to allow using it in the 2D drawing interface.
class FWrapperTexture : public FTexture
{
	int Format;
public:
	FWrapperTexture(int w, int h, int bits = 1);
	IHardwareTexture *GetSystemTexture()
	{
		return SystemTextures.GetHardwareTexture(0, 0);
	}

	int GetColorFormat() const
	{
		return Format;
	}
};


class FImageTexture : public FTexture
{
	FImageSource* mImage;
	bool bNoRemap0 = false;
	int TexFrame = 0;
protected:
	void SetFromImage();
public:
	FImageTexture(FImageSource* image, int frame = 0) noexcept;
	~FImageTexture();
	TArray<uint8_t> Get8BitPixels(bool alphatex) override;

	void SetImage(FImageSource* img)
	{
		mImage = img;
		SetFromImage();
	}
	void SetNoRemap0() { bNoRemap0 = true; }

	FImageSource* GetImage() const override { return mImage; }
	FBitmap GetBgraBitmap(const PalEntry* p, int* trans) override;
	bool DetermineTranslucency() override;

};


#include "gametexture.h"
#endif


