//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#ifndef MAPFACE_H
#define MAPFACE_H

#ifdef _WIN32
#pragma once
#endif


#pragma warning(push, 1)
#pragma warning(disable:4701 4702 4530)
#include <fstream>
#pragma warning(pop)
#include "hammer_mathlib.h"
#include "MapAtom.h"
#include "DispManager.h"
#include "mathlib/Vector4d.h"
#include "utlvector.h"
#include "Color.h"
#include "smoothinggroupmgr.h"
#include "detailobjects.h"

class CCheckFaceInfo;
class IEditorTexture;
class CRender;
class CRender3D;
class CChunkFile;
class CSaveInfo;
class IMaterial;
class CMapWorld;
struct MapFaceRender_t;
class CMeshBuilder;
class IMesh;

struct LoadFace_t;

enum EditorRenderMode_t;
enum ChunkFileResult_t;

#define DEFAULT_TEXTURE_SCALE			0.25
#define DEFAULT_LIGHTMAP_SCALE			16

#define SMOOTHING_GROUP_MAX_COUNT		32
#define SMOOTHING_GROUP_DEFAULT			0

//
// Flags for CMapFace::CopyFrom.
//
#define COPY_FACE_PLANE			0x00000001			// Copies only the face's plane. Used for carving.
#define COPY_FACE_POINTS		0x00000002			// Copies the face's points and plane.


//
// Used for storing the extrema of a face. Each element of the Extents_t array
// contains a point that represents a local extreme along a particular dimension.
//
enum
{
	EXTENTS_XMIN = 0,
	EXTENTS_XMAX,
	EXTENTS_YMIN,
	EXTENTS_YMAX,
	EXTENTS_ZMIN,
	EXTENTS_ZMAX,
	NUM_EXTENTS_DIMS,
};

typedef Vector Extents_t[NUM_EXTENTS_DIMS];


struct PLANE
{
	Vector		normal;
	float		dist;
	Vector		planepts[3];
};


typedef struct
{
	int		numpoints;
	Vector	*p;			// variable sized
} winding_t;


enum FaceOrientation_t
{
	FACE_ORIENTATION_FLOOR = 0,
	FACE_ORIENTATION_CEILING,
	FACE_ORIENTATION_NORTH_WALL,
	FACE_ORIENTATION_SOUTH_WALL,
	FACE_ORIENTATION_EAST_WALL,
	FACE_ORIENTATION_WEST_WALL,
	FACE_ORIENTATION_INVALID
};


//
// Both an enumeration and bitflags. Used as bitflags when querying a face for its texture
// alignment because it could be world aligned and face aligned at the same time.
//
enum TextureAlignment_t
{
	TEXTURE_ALIGN_NONE	= 0x0000,
	TEXTURE_ALIGN_WORLD = 0x0001,
	TEXTURE_ALIGN_FACE	= 0x0002,
	TEXTURE_ALIGN_QUAKE = 0x0004
};


enum TextureJustification_t
{
	TEXTURE_JUSTIFY_NONE = 0,
	TEXTURE_JUSTIFY_TOP,
	TEXTURE_JUSTIFY_BOTTOM,
	TEXTURE_JUSTIFY_LEFT,
	TEXTURE_JUSTIFY_CENTER,
	TEXTURE_JUSTIFY_RIGHT,
	TEXTURE_JUSTIFY_FIT,
	TEXTURE_JUSTIFY_MAX
};


#define INIT_TEXTURE_FORCE			0x0001
#define INIT_TEXTURE_AXES			0x0002
#define INIT_TEXTURE_ROTATION		0x0004
#define INIT_TEXTURE_SHIFT			0x0008
#define INIT_TEXTURE_SCALE			0x0010
#define INIT_TEXTURE_ALL			(INIT_TEXTURE_AXES | INIT_TEXTURE_ROTATION | INIT_TEXTURE_SHIFT | INIT_TEXTURE_SCALE)


//
// Flags for CreateFace.
//
#define CREATE_FACE_PRESERVE_PLANE	0x0001		// Hack to prevent plane from being recalculated while building a solid from its planes.
#define CREATE_FACE_CLIPPING		0x0002

//
// Serialized data structure. Do not modify!
//
struct TEXTURE_21
{
	char	texture[MAX_PATH];
	float	rotate;
	float	shift[2];
	float	scale[2];
	BYTE	smooth;
	BYTE	material;
	DWORD	q2surface;
	DWORD	q2contents;
	DWORD	q2value;
};


//
// Post 2.1 explicit texture U/V axes were added.
//
// Serialized data structure. Do not modify!
//
struct TEXTURE_33
{
	char texture[MAX_PATH];
	float UAxis[4];				// Must remain float[4] for RMF serialization.
	float VAxis[4];				// Must remain float[4] for RMF serialization.
	float rotate;
	float scale[2];
	BYTE smooth;
	BYTE material;
	DWORD q2surface;
	DWORD q2contents;
	int nLightmapScale;
};


struct TEXTURE
{
	char texture[MAX_PATH];
	Vector4D UAxis;
	Vector4D VAxis;
	float rotate;
	float scale[2];
	BYTE smooth;
	BYTE material;
	DWORD q2surface;
	DWORD q2contents;
	int nLightmapScale;

	TEXTURE& operator=( TEXTURE const& src )
	{
		// necessary since operator= is private for UAxis
		memcpy( this, &src, sizeof(TEXTURE) );
		return *this;
	}
};


#define FACE_FLAGS_NOSHADOW 1
#define FACE_FLAGS_NODRAW_IN_LPREVIEW 2



class CMapFace : public CMapAtom
{
public:

	CMapFace(void);
	~CMapFace(void);

	// If bRescaleTextureCoordinates is true, then it will rescale and reoffset the texture coordinates
	// so that the texture is in the same apparent spot as the old texture (if they are different sizes).
	void SetTexture(const char *pszNewTex, bool bRescaleTextureCoordinates = false);
	void SetTexture(IEditorTexture *pTexture, bool bRescaleTextureCoordinates = false);
	void GetTextureName(char *pszName) const;

	inline IEditorTexture *GetTexture(void) const;
		
	// Renders opaque faces
	static void			AddFaceToQueue( CMapFace* pMapFace, IEditorTexture* pTexture, EditorRenderMode_t renderMode, bool selected, SelectionState_t faceSelectionState );
	static void			PushFaceQueue( void );
	static void			PopFaceQueue( void );
	static void			RenderOpaqueFaces( CRender3D* pRender );

	//
	// Serialization.
	//
	ChunkFileResult_t LoadVMF(CChunkFile *pFile);
	ChunkFileResult_t SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo);
	int SerializeRMF(std::fstream&, BOOL);
	int SerializeMAP(std::fstream&, BOOL);

	BOOL CheckFace(CCheckFaceInfo* = NULL);
	BOOL Fix(void);

	float GetNormalDistance(Vector& fPoint);

	inline int GetPointCount(void);
	inline void GetPoint(Vector& Point, int nPoint);

	inline void GetLightmapCoord( Vector2D & LightmapCoord, int nIndex );
	inline void SetLightmapCoord( const Vector2D &LightmapCoord, int nIndex );
	inline void GetTexCoord( Vector2D & TexCoord, int nTexCoord );

	// Texture alignment.
	void GetCenter(Vector& Center);
	FaceOrientation_t GetOrientation(void) const;
	void RotateTextureAxes(float fDegrees);
	void InitializeTextureAxes(TextureAlignment_t eAlignment, DWORD dwFlags);
	void JustifyTexture(TextureJustification_t eJustification);
	void JustifyTextureUsingExtents(TextureJustification_t eJustification, Extents_t Extents);
	void GetFaceBounds(Vector& pfMins, Vector& pfMaxs) const;
	void GetFaceExtents(Extents_t Extents) const;
	void GetTextureExtents(Extents_t Extents, Vector2D & TopLeft, Vector2D & BottomRight) const;
	int GetTextureAlignment(void) const;
	void GetFaceTextureExtents(Vector2D & TopLeft, Vector2D & BottomRight) const;

	void CalcTextureCoordAtPoint( const Vector& pt, Vector2D & texCoord );
	void CalcLightmapCoordAtPoint( const Vector& pt, Vector2D & lightCoord );

	// Returns the max lightmap size for this face
	int MaxLightmapSize() const;

	void NormalizeTextureShifts(void);
	BOOL IsTextureAxisValid(void) const;

	inline void SetCordonFace( bool bCordonFace );
	inline bool IsCordonFace() const;

	// Old code for setting up texture axes. Needed for backwards compatibility.
	void InitializeQuakeStyleTextureAxes(Vector4D& UAxis, Vector4D& VAxis);

	void CreateFace(Vector *pPoints, int nPoints, bool bIsCordonFace = false);
	void CreateFace(winding_t *w, int nFlags = 0);
	CMapFace *CopyFrom(const CMapFace *pFrom, DWORD dwFlags = COPY_FACE_POINTS, bool bUpdateDependencies = true );
	size_t AllocatePoints(int nPoints);

	void OnUndoRedo();

	void CalcPlane(void);
	void CalcPlaneFromFacePoints(void);

	void CalcTextureCoords();
	void OffsetTexture(const Vector &Delta);
	void SetTextureCoords(int nPoint, float u, float v);

	struct TangentSpaceAxes_t
	{
		Vector	tangent;
		Vector	binormal;
	};
		
	void CalcTangentSpaceAxes( void );
	bool AllocTangentSpaceAxes( int count );
	void FreeTangentSpaceAxes( void );

	void Render2D(CRender2D *pRender);
	void Render3D(CRender3D *pRender);
	void Render3DGrid(CRender3D *pRender);
	void RenderVertices(CRender *pRender);

	void OnAddToWorld(CMapWorld *pWorld);
	void OnRemoveFromWorld(void);

	static void SetShowSelection(bool bShowSelection);

	inline void SetRenderAlpha(unsigned char uchAlpha) { m_uchAlpha = uchAlpha; } // HACK: should be in CMapAtom

	inline void GetFaceNormal( Vector& normal );
	bool TraceLine(Vector &HitPos, Vector &HitNormal, Vector const &Start, Vector const &End);
	bool TraceLineInside( Vector &HitPos, Vector &HitNormal, Vector const &Start, Vector const &End, bool bNoDisp = false );

	inline void SetDisp( EditDispHandle_t handle, bool bDestroyPrevious = true );
	inline EditDispHandle_t GetDisp( void );
	inline bool HasDisp( void ) const;

	bool ShouldRenderLast();
	void GetDownVector( int index, Vector& downVect );

	bool GetRender2DBox( Vector& boundMin, Vector& boundMax );
	bool GetCullBox( Vector& boundMin, Vector& boundMax );
	size_t GetDataSize( void );

	inline int GetFaceID(void);
	inline void SetFaceID(int nFaceID);

	// Smoothing group.
	int SmoothingGroupCount( void );
	void AddSmoothingGroup( int iGroup );
	void RemoveSmoothingGroup( int iGroup );
	bool InSmoothingGroup( int iGroup );

	// Indicates this guy should be unlit
	void RenderUnlit( bool enable );

	// (begin serialized information
	TEXTURE texture;					// Texture info.
	Vector *Points;						// Array of face points, dynamically allocated.
	int	nPoints;						// The number of points in the array.
	// end serialized information)

	PLANE plane;

	int m_nFaceFlags;										// FACE_FLAGS_xx

	void DoTransform(const VMatrix &matrix);

	virtual void AddShadowingTriangles( CUtlVector<Vector> &tri_list );

	DetailObjects		*m_pDetailObjects;
	
protected:

	void ComputeColor( CRender3D* pRender, bool bRenderAsSelected, SelectionState_t faceSelectionState,
					   bool ignoreLighting, Color &pColor );

	void DrawFace( Color &pColor, EditorRenderMode_t mode );
	void RenderGridIfCloseEnough( CRender3D* pRender );
	void RenderTextureAxes( CRender3D* pRender );

	// Adds a face's vertices to the meshbuilder
	void AddFaceVertices( CMeshBuilder &builder, CRender3D* pRender, bool bRenderSelected, SelectionState_t faceSelectionState );

	// render texture axes
	static void RenderTextureAxes( CRender3D* pRender, int nCount, CMapFace **ppFaces );
	static void RenderGridsIfCloseEnough( CRender3D* pRender, int nCount, CMapFace **ppFaces );
	static void Render3DGrids( CRender3D* pRender, int nCount, CMapFace **ppFaces );
	static void RenderWireframeFaces( CRender3D* pRender, int nCount, MapFaceRender_t **ppFaces );
	static void RenderFacesBatch( CMeshBuilder &MeshBuilder, IMesh* pMesh, CRender3D* pRender, MapFaceRender_t **ppFaces, int nFaceCount, int nVertexCount, int nIndexCount, bool bWireframe );
	static void RenderFaces( CRender3D* pRender, int nCount, MapFaceRender_t **ppFaces );

	void RenderFace3D( CRender3D* pRender, EditorRenderMode_t renderMode, bool renderSelected, SelectionState_t faceSelectionState );

	//
	// Serialization (chunk handlers).
	//
	static ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, CMapFace *pFace);
	static ChunkFileResult_t LoadKeyCallback(const char *szKey, const char *szValue, LoadFace_t *pLoadFace);

	unsigned char m_uchAlpha;			// HACK: should be in CMapAtom

	int m_nFaceID;						// The unique ID of this face in the world.

	IEditorTexture *m_pTexture;				// Texture that is applied to this face.
	static IEditorTexture *m_pLightmapGrid;	// Lightmap grid texture for use in viewing lightmap scales.
	EditDispHandle_t	m_DispHandle;			// Displacement map applied to this face, NULL if none.

	static bool m_bShowFaceSelection;	// Whether to render faces with a special color when they are selected.

	Vector2D *m_pTextureCoords;			// An array of texture coordinates, one per face point.
	Vector2D *m_pLightmapCoords;			// An array of lightmap coordinates, one per face point.

	bool m_bIsCordonFace : 1;

	// should this be affected by lighting?
	bool m_bIgnoreLighting : 1;

	TangentSpaceAxes_t	*m_pTangentAxes;

	unsigned int		m_fSmoothingGroups;		// 32-bits representing 32 smoothing groups

	void UpdateFaceFlags( void );							// sniff face flags from texture
};


//-----------------------------------------------------------------------------
// Purpose: Returns the unique ID of this face.
//-----------------------------------------------------------------------------
inline int CMapFace::GetFaceID(void)
{
	return(m_nFaceID);
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : TexCoord - 
//			nTexCoord - 
//-----------------------------------------------------------------------------
inline void CMapFace::GetLightmapCoord( Vector2D& LightmapCoord, int nIndex )
{
    Assert( nIndex < nPoints );
    LightmapCoord[0] = m_pLightmapCoords[nIndex][0];
    LightmapCoord[1] = m_pLightmapCoords[nIndex][1];
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &LightmapCoord - 
//			nIndex - 
// Output : inline void
//-----------------------------------------------------------------------------
inline void CMapFace::SetLightmapCoord( const Vector2D &LightmapCoord, int nIndex )
{
    Assert( nIndex < nPoints );
	m_pLightmapCoords[nIndex][0] = LightmapCoord[0];
	m_pLightmapCoords[nIndex][1] = LightmapCoord[1];
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : TexCoord - 
//			nTexCoord - 
//-----------------------------------------------------------------------------
inline void CMapFace::GetTexCoord( Vector2D& TexCoord, int nTexCoord )
{
    Assert( nTexCoord < nPoints );
    TexCoord[0] = m_pTextureCoords[nTexCoord][0];
    TexCoord[1] = m_pTextureCoords[nTexCoord][1];
}


//-----------------------------------------------------------------------------
// Purpose: Returns a pointer to the texture that is applied to this face, NULL if none.
//-----------------------------------------------------------------------------
inline IEditorTexture *CMapFace::GetTexture(void) const
{
	return(m_pTexture);
}


//-----------------------------------------------------------------------------
// Purpose: Returns the number of vertices that define this face.
//-----------------------------------------------------------------------------
inline int CMapFace::GetPointCount(void)
{
	return(nPoints);
}


//-----------------------------------------------------------------------------
// Purpose: Retrieves a point on this face by its index.
// Input  : Point - Receives point coordinates.
//			nPoint - Index of point to retrieve.
//-----------------------------------------------------------------------------
inline void CMapFace::GetPoint(Vector& Point, int nPoint)
{
	Assert(nPoint < nPoints);
	Point = Points[nPoint];
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CMapFace::GetFaceNormal( Vector& normal )
{
	normal = plane.normal;
}


//-----------------------------------------------------------------------------
// Purpose: Sets the unique ID of this face.
//-----------------------------------------------------------------------------
inline void CMapFace::SetFaceID(int nID)
{
	m_nFaceID = nID;
}


//-----------------------------------------------------------------------------
// Purpose: Attaches a displacement surface to this face.
// Input  : handle - Displacement surface handle of surface attached to this face
//-----------------------------------------------------------------------------
inline void CMapFace::SetDisp( EditDispHandle_t handle, bool bDestroyPrevious )
{
	if ( ( m_DispHandle != EDITDISPHANDLE_INVALID ) && bDestroyPrevious )
	{
		// destroy old handle
		EditDispMgr()->Destroy( m_DispHandle );
	}
		
	Assert( ( handle == EDITDISPHANDLE_INVALID ) || ( EditDispMgr()->GetDisp( handle ) != NULL ) );

	m_DispHandle = handle;
}


//-----------------------------------------------------------------------------
// Purpose: Returns the displacement surface applied to this face, 
//          DISPHANDLE_INVALID if none.
//-----------------------------------------------------------------------------
inline EditDispHandle_t CMapFace::GetDisp( void )
{
    return m_DispHandle;
}


//-----------------------------------------------------------------------------
// Purpose: Returns true if this face has a displacement surface, false if not.
//-----------------------------------------------------------------------------
inline bool CMapFace::HasDisp( void ) const
{
	return ( m_DispHandle != EDITDISPHANDLE_INVALID );
}


//-----------------------------------------------------------------------------
// Whether this face belongs to a cordon brush.
//-----------------------------------------------------------------------------
inline void CMapFace::SetCordonFace( bool bCordonFace )
{
	m_bIsCordonFace = bCordonFace;
}


inline bool CMapFace::IsCordonFace() const
{
	return m_bIsCordonFace;
}


//-----------------------------------------------------------------------------
// Defines a container class for a list of face pointers.
//-----------------------------------------------------------------------------
class CMapFaceList : public CUtlVector<CMapFace *>
{
public:

	inline CMapFaceList(void) {}
	inline CMapFaceList(CMapFaceList const &other);
	inline CMapFaceList &CMapFaceList::operator =(CMapFaceList const &other);

	inline int FindFaceID(int nFaceID);
	void Intersect(CMapFaceList &IntersectWith, CMapFaceList &In, CMapFaceList &Out);
};


//-----------------------------------------------------------------------------
// Purpose: Copy constructor.
//-----------------------------------------------------------------------------
CMapFaceList::CMapFaceList(CMapFaceList const &other)
{
	*this = other;
}


//-----------------------------------------------------------------------------
// Purpose: Assignment operator for copying face lists.
// Input  : other - 
//-----------------------------------------------------------------------------
CMapFaceList &CMapFaceList::operator =(CMapFaceList const &other)
{
	AddVectorToTail(other);
	return *this;
}


//-----------------------------------------------------------------------------
// Purpose: Searches the list for a face with the given ID.
// Input  : nFaceID - Numeric face ID to search for.
// Output : Index of found element, -1 if none.
//-----------------------------------------------------------------------------
int CMapFaceList::FindFaceID(int nFaceID)
{
	for (int i = 0; i < Count(); i++)
	{
		if ((Element(i) != NULL) && (Element(i)->GetFaceID() == nFaceID))
		{
			return(i);
		}
	}

	return(-1);
}


//-----------------------------------------------------------------------------
// Defines a container class for a list of face IDs.
//-----------------------------------------------------------------------------
class CMapFaceIDList : public CUtlVector<int>
{
public:

	inline CMapFaceIDList(void) {}
	inline CMapFaceIDList(CMapFaceIDList const &other);
	inline CMapFaceIDList &CMapFaceIDList::operator =(CMapFaceIDList const &other);

	void Intersect(CMapFaceIDList &IntersectWith, CMapFaceIDList &In, CMapFaceIDList &Out);
};


//-----------------------------------------------------------------------------
// Purpose: Copy constructor.
//-----------------------------------------------------------------------------
CMapFaceIDList::CMapFaceIDList(CMapFaceIDList const &other)
{
	*this = other;
}


//-----------------------------------------------------------------------------
// Purpose: Assignment operator for copying face ID lists.
// Input  : other - 
//-----------------------------------------------------------------------------
CMapFaceIDList &CMapFaceIDList::operator =(CMapFaceIDList const &other)
{
	AddVectorToTail(other);
	return *this;
}


#endif // MAPFACE_H