//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Local header for CVTFTexture class declaration - allows platform-specific
//			implementation to be placed in separate cpp files.
//
// $NoKeywords: $
//===========================================================================//

#ifndef CVTF_H
#define CVTF_H

#ifdef _WIN32
#pragma once
#endif

#include "s3tc_decode.h"
#include "vtf/vtf.h"
#include "byteswap.h"
#include "filesystem.h"

class CEdgePos
{
public:
	CEdgePos() = default;
	CEdgePos( int ix, int iy )
	{
		x = ix;
		y = iy;
	}

	void operator +=( const CEdgePos &other ) 
	{ 
		x += other.x;
		y += other.y;
	}

	void operator /=( int val )
	{ 
		x /= val;
		y /= val;
	}

	CEdgePos operator >>( int shift )
	{ 
		return CEdgePos( x >> shift, y >> shift );
	}

	CEdgePos operator *( int shift )
	{ 
		return CEdgePos( x * shift, y * shift );
	}

	CEdgePos operator -( const CEdgePos &other ) 
	{ 
		return CEdgePos( x - other.x, y - other.y );
	}

	CEdgePos operator +( const CEdgePos &other ) 
	{ 
		return CEdgePos( x + other.x, y + other.y );
	}

	bool operator!=( const CEdgePos &other )
	{
		return !( *this == other );
	}

	bool operator==( const CEdgePos &other )
	{
		return x==other.x && y==other.y;
	}

	int x, y;
};


class CEdgeIncrements
{
public:
	CEdgePos iFace1Start, iFace1End;
	CEdgePos iFace1Inc, iFace2Inc;
	CEdgePos iFace2Start, iFace2End;
};


class CEdgeMatch
{
public:
	int m_iFaces[2];	// Which faces are touching.
	int m_iEdges[2];	// Which edge on each face is touching.
	int m_iCubeVerts[2];// Which of the cube's verts comprise this edge?
	bool m_bFlipFace2Edge;
};


class CCornerMatch
{
public:
	// The info for the 3 edges that match at this corner.
	int m_iFaces[3];
	int m_iFaceEdges[3];
};


class CEdgeFaceIndex
{
public:
	int m_iEdge;
	int m_iFace;
};


#define NUM_EDGE_MATCHES	12
#define NUM_CORNER_MATCHES	8


//-----------------------------------------------------------------------------
// Implementation of the VTF Texture
//-----------------------------------------------------------------------------
class CVTFTexture : public IVTFTexture
{
public:
	CVTFTexture();
	virtual ~CVTFTexture();

	virtual bool Init( int nWidth, int nHeight, int nDepth, ImageFormat fmt, int iFlags, int iFrameCount, int nForceMipCount );

	// Methods to initialize the low-res image
	virtual void InitLowResImage( int nWidth, int nHeight, ImageFormat fmt );

	virtual void *SetResourceData( uint32 eType, void const *pData, size_t nDataSize );
	virtual void *GetResourceData( uint32 eType, size_t *pDataSize ) const;

	// Locates the resource entry info if it's present, easier than crawling array types
	virtual bool HasResourceEntry( uint32 eType ) const;

	// Retrieve available resource types of this IVTFTextures
	//		arrTypesBuffer			buffer to be filled with resource types available.
	//		numTypesBufferElems		how many resource types the buffer can accomodate.
	// Returns:
	//		number of resource types available (can be greater than "numTypesBufferElems"
	//		in which case only first "numTypesBufferElems" are copied to "arrTypesBuffer")
	virtual unsigned int GetResourceTypes( uint32 *arrTypesBuffer, int numTypesBufferElems ) const;

	// Methods to set other texture fields
	virtual void SetBumpScale( float flScale );
	virtual void SetReflectivity( const Vector &vecReflectivity );

	// These are methods to help with optimization of file access
	virtual void LowResFileInfo( int *pStartLocation, int *pSizeInBytes ) const;
	virtual void ImageFileInfo( int nFrame, int nFace, int nMip, int *pStartLocation, int *pSizeInBytes) const;
	virtual int FileSize( int nMipSkipCount = 0 ) const;

	// When unserializing, we can skip a certain number of mip levels,
	// and we also can just load everything but the image data
	virtual bool Unserialize( CUtlBuffer &buf, bool bBufferHeaderOnly = false, int nSkipMipLevels = 0 );
	virtual bool UnserializeEx( CUtlBuffer &buf, bool bHeaderOnly = false, int nForceFlags = 0, int nSkipMipLevels = 0 );
	virtual bool Serialize( CUtlBuffer &buf );

	virtual void GetMipmapRange( int* pOutFinest, int* pOutCoarsest );

	// Attributes...
	virtual int Width() const;
	virtual int Height() const;
	virtual int Depth() const;
	virtual int MipCount() const;

	virtual int RowSizeInBytes( int nMipLevel ) const;
	virtual int FaceSizeInBytes( int nMipLevel ) const;

	virtual ImageFormat Format() const;
	virtual int FaceCount() const;
	virtual int FrameCount() const;
	virtual int Flags() const;

	virtual float BumpScale() const;
	virtual const Vector &Reflectivity() const;

	virtual bool IsCubeMap() const;
	virtual bool IsNormalMap() const;
	virtual bool IsVolumeTexture() const;

	virtual int LowResWidth() const;
	virtual int LowResHeight() const;
	virtual ImageFormat LowResFormat() const;

	// Computes the size (in bytes) of a single mipmap of a single face of a single frame 
	virtual int ComputeMipSize( int iMipLevel ) const;

	// Computes the size (in bytes) of a single face of a single frame
	// All mip levels starting at the specified mip level are included
	virtual int ComputeFaceSize( int iStartingMipLevel = 0 ) const;

	// Computes the total size of all faces, all frames
	virtual int ComputeTotalSize( ) const;

	// Computes the dimensions of a particular mip level
	virtual void ComputeMipLevelDimensions( int iMipLevel, int *pWidth, int *pHeight, int *pMipDepth ) const;

	// Computes the size of a subrect (specified at the top mip level) at a particular lower mip level
	virtual void ComputeMipLevelSubRect( Rect_t* pSrcRect, int nMipLevel, Rect_t *pSubRect ) const;

	// Returns the base address of the image data
	virtual unsigned char *ImageData();

	// Returns a pointer to the data associated with a particular frame, face, and mip level
	virtual unsigned char *ImageData( int iFrame, int iFace, int iMipLevel );

	// Returns a pointer to the data associated with a particular frame, face, mip level, and offset
	virtual unsigned char *ImageData( int iFrame, int iFace, int iMipLevel, int x, int y, int z );

	// Returns the base address of the low-res image data
	virtual unsigned char *LowResImageData();

	// Converts the texture's image format. Use IMAGE_FORMAT_DEFAULT
	virtual void ConvertImageFormat( ImageFormat fmt, bool bNormalToDUDV );

	// Generate spheremap based on the current cube faces (only works for cubemaps)
	// The look dir indicates the direction of the center of the sphere
	virtual void GenerateSpheremap( LookDir_t lookDir );

	virtual void GenerateHemisphereMap( unsigned char *pSphereMapBitsRGBA, int targetWidth, 
		int targetHeight, LookDir_t lookDir, int iFrame );

	// Fixes the cubemap faces orientation from our standard to the
	// standard the material system needs.
	virtual void FixCubemapFaceOrientation( );

	// Normalize the top mip level if necessary
	virtual void NormalizeTopMipLevel();
	
	// Generates mipmaps from the base mip levels
	virtual void GenerateMipmaps();

	// Put 1/miplevel (1..n) into alpha.
	virtual void PutOneOverMipLevelInAlpha();

	// Computes the reflectivity
	virtual void ComputeReflectivity( );

	// Computes the alpha flags
	virtual void ComputeAlphaFlags();

	// Gets the texture all internally consistent assuming you've loaded
	// mip 0 of all faces of all frames
	virtual void PostProcess(bool bGenerateSpheremap, LookDir_t lookDir = LOOK_DOWN_Z, bool bAllowFixCubemapOrientation = true);
	virtual void SetPostProcessingSettings( VtfProcessingOptions const *pOptions );

	// Generate the low-res image bits
	virtual bool ConstructLowResImage();

	virtual void MatchCubeMapBorders( int iStage, ImageFormat finalFormat, bool bSkybox );

	// Sets threshhold values for alphatest mipmapping
	virtual void SetAlphaTestThreshholds( float flBase, float flHighFreq );

#if defined( _X360 )
	virtual int UpdateOrCreate( const char *pFilename, const char *pPathID = NULL, bool bForce = false );
	virtual int FileSize( bool bPreloadOnly, int nMipSkipCount ) const;
	virtual bool UnserializeFromBuffer( CUtlBuffer &buf, bool bBufferIsVolatile, bool bHeaderOnly, bool bPreloadOnly, int nMipSkipCount );
	virtual bool IsPreTiled() const;
	virtual int MappingWidth() const;
	virtual int MappingHeight() const;
	virtual int MappingDepth() const;
	virtual int MipSkipCount() const;
	virtual unsigned char *LowResImageSample();
	virtual void ReleaseImageMemory();
#endif

private:
	// Unserialization
	bool ReadHeader( CUtlBuffer &buf, VTFFileHeader_t &header );

	void BlendCubeMapEdgePalettes(
		int iFrame,
		int iMipLevel,
		const CEdgeMatch *pMatch );

	void BlendCubeMapCornerPalettes(
		int iFrame,
		int iMipLevel,
		const CCornerMatch *pMatch );

	void MatchCubeMapS3TCPalettes(
		CEdgeMatch edgeMatches[NUM_EDGE_MATCHES],
		CCornerMatch cornerMatches[NUM_CORNER_MATCHES]
		);
	
	void SetupFaceVert( int iMipLevel, int iVert, CEdgePos &out );
	void SetupEdgeIncrement( CEdgePos &start, CEdgePos &end, CEdgePos &inc );

	void SetupTextureEdgeIncrements( 
		int iMipLevel,
		int iFace1Edge,
		int iFace2Edge,
		bool bFlipFace2Edge,
		CEdgeIncrements *incs );
	
	void BlendCubeMapFaceEdges(
		int iFrame,
		int iMipLevel,
		const CEdgeMatch *pMatch );

	void BlendCubeMapFaceCorners(
		int iFrame,
		int iMipLevel,
		const CCornerMatch *pMatch );

	void BuildCubeMapMatchLists( CEdgeMatch edgeMatches[NUM_EDGE_MATCHES], CCornerMatch cornerMatches[NUM_CORNER_MATCHES], bool bSkybox );

	// Allocate image data blocks with an eye toward re-using memory
	bool AllocateImageData( int nMemorySize );
	bool AllocateLowResImageData( int nMemorySize );

	// Compute the mip count based on the size + flags
	int ComputeMipCount( ) const;

	// Unserialization of low-res data
	bool LoadLowResData( CUtlBuffer &buf );

	// Unserialization of new resource data
	bool LoadNewResources( CUtlBuffer &buf );

	// Unserialization of image data
	bool LoadImageData( CUtlBuffer &buf, const VTFFileHeader_t &header, int nSkipMipLevels );

	// Shutdown
	void Shutdown();
	void ReleaseResources();

	// Makes a single frame of spheremap
	void ComputeSpheremapFrame( unsigned char **ppCubeFaces, unsigned char *pSpheremap, LookDir_t lookDir );

	// Makes a single frame of spheremap
	void ComputeHemispheremapFrame( unsigned char **ppCubeFaces, unsigned char *pSpheremap, LookDir_t lookDir );

	// Serialization of image data
	bool WriteImageData( CUtlBuffer &buf );

	// Computes the size (in bytes) of a single mipmap of a single face of a single frame 
	int ComputeMipSize( int iMipLevel, ImageFormat fmt ) const;

	// Computes the size (in bytes) of a single face of a single frame
	// All mip levels starting at the specified mip level are included
	int ComputeFaceSize( int iStartingMipLevel, ImageFormat fmt ) const;

	// Computes the total size of all faces, all frames
	int ComputeTotalSize( ImageFormat fmt ) const;

	// Computes the location of a particular face, frame, and mip level
	int GetImageOffset( int iFrame, int iFace, int iMipLevel, ImageFormat fmt ) const;

	// Determines if the vtf or vtfx file needs to be swapped to the current platform
	bool SetupByteSwap( CUtlBuffer &buf );

	// Locates the resource entry info if it's present
	ResourceEntryInfo *FindResourceEntryInfo( unsigned int eType );
	ResourceEntryInfo const *FindResourceEntryInfo( unsigned int eType ) const;
	
	// Inserts the resource entry info if it's not present
	ResourceEntryInfo *FindOrCreateResourceEntryInfo( unsigned int eType );

	// Removes the resource entry info if it's present
	bool RemoveResourceEntryInfo( unsigned int eType );

#if defined( _X360 )
	bool ReadHeader( CUtlBuffer &buf, VTFFileHeaderX360_t &header );
	bool LoadImageData( CUtlBuffer &buf, bool bBufferIsVolatile, int nMipSkipCount );
#endif

private:
	// This is to make sure old-format .vtf files are read properly
	int				m_nVersion[2];

	int				m_nWidth;
	int				m_nHeight;
	int 			m_nDepth;
	ImageFormat		m_Format;

	int				m_nMipCount;
	int				m_nFaceCount;
	int				m_nFrameCount;

	int				m_nImageAllocSize;
	int				m_nFlags;
	unsigned char	*m_pImageData;

	Vector			m_vecReflectivity;
	float			m_flBumpScale;
	
	// FIXME: Do I need this?
	int				m_iStartFrame;

	// Low res data
	int				m_nLowResImageAllocSize;
	ImageFormat		m_LowResImageFormat;
	int				m_nLowResImageWidth;
	int				m_nLowResImageHeight;
	unsigned char	*m_pLowResImageData;

	// Used while fixing mipmap edges.
	CUtlVector<S3RGBA> m_OriginalData;

	// Alpha threshholds
	float			m_flAlphaThreshhold;
	float			m_flAlphaHiFreqThreshhold;

	CByteswap		m_Swap;

	int				m_nFinestMipmapLevel;
	int				m_nCoarsestMipmapLevel;

#if defined( _X360 )
	int				m_iPreloadDataSize;
	int				m_iCompressedSize;
	// resolves actual dimensions to/from mapping dimensions due to pre-picmipping
	int				m_nMipSkipCount;
	unsigned char	m_LowResImageSample[4];
#endif

	CUtlVector< ResourceEntryInfo > m_arrResourcesInfo;

	struct ResourceMemorySection
	{
		ResourceMemorySection() { memset( this, 0, sizeof( *this ) ); }

		int				m_nDataAllocSize;
		int				m_nDataLength;
		unsigned char	*m_pData;

		bool AllocateData( int nMemorySize );
		bool LoadData( CUtlBuffer &buf, CByteswap &byteSwap );
		bool WriteData( CUtlBuffer &buf ) const;
	};
	CUtlVector< ResourceMemorySection > m_arrResourcesData;
	CUtlVector< ResourceMemorySection > m_arrResourcesData_ForReuse;	// Maintained to keep allocated memory blocks when unserializing from files

	VtfProcessingOptions m_Options;
};

#endif	// CVTF_H