#ifndef MESHREADER_H
#define MESHREADER_H

#ifdef _WIN32
#pragma once
#endif


class CBaseMeshReader : protected MeshDesc_t
{
public:

	CBaseMeshReader();
	~CBaseMeshReader();

	void BeginRead(
		IMesh* pMesh,
		int firstVertex = 0,
		int numVertices = 0,
		int firstIndex = 0,
		int numIndices = 0);

	void EndRead();

	void BeginRead_Direct(const MeshDesc_t& desc, int numVertices, int nIndices);

	void Reset();


protected:
	IMesh* m_pMesh;
	int m_MaxVertices;
	int m_MaxIndices;
};


class CMeshReader : public CBaseMeshReader
{
public:
public:
	int NumIndices() const;
	unsigned short Index(int index) const;

	const Vector& Position(int iVertex) const;

	unsigned int Color(int iVertex) const;

	const float* TexCoord(int iVertex, int stage) const;
	void TexCoord2f(int iVertex, int stage, float& s, float& t) const;
	const Vector2D& TexCoordVector2D(int iVertex, int stage) const;

	int NumBoneWeights() const;
	float Wrinkle(int iVertex) const;

	const Vector& Normal(int iVertex) const;
	void Normal(int iVertex, Vector& vNormal) const;

	const Vector& TangentS(int iVertex) const;
	const Vector& TangentT(int iVertex) const;
	float BoneWeight(int iVertex) const;

#ifdef NEW_SKINNING
	float* BoneMatrix(int iVertex) const;
#else
	unsigned char* BoneMatrix(int iVertex) const;
#endif
};


inline CBaseMeshReader::CBaseMeshReader()
{
	m_pMesh = NULL;
}

inline CBaseMeshReader::~CBaseMeshReader()
{
	Assert(!m_pMesh);
}

inline void CBaseMeshReader::BeginRead(
	IMesh* pMesh,
	int firstVertex,
	int numVertices,
	int firstIndex,
	int numIndices)
{
	Assert(pMesh && (!m_pMesh));

	if (numVertices < 0)
	{
		numVertices = pMesh->VertexCount();
	}

	if (numIndices < 0)
	{
		numIndices = pMesh->IndexCount();
	}

	m_pMesh = pMesh;
	m_MaxVertices = numVertices;
	m_MaxIndices = numIndices;

	VertexCompressionType_t compressionType = CompressionType(pMesh->GetVertexFormat());
	Assert(compressionType == VERTEX_COMPRESSION_NONE);
	if (compressionType != VERTEX_COMPRESSION_NONE)
	{
		Warning("Cannot use CBaseMeshReader with compressed vertices! Will get junk data or a crash.\n");
	}

	pMesh->ModifyBeginEx(true, firstVertex, numVertices, firstIndex, numIndices, *this);

	Reset();
}

inline void CBaseMeshReader::EndRead()
{
	Assert(m_pMesh);
	m_pMesh->ModifyEnd(*this);
	m_pMesh = NULL;
}

inline void CBaseMeshReader::BeginRead_Direct(const MeshDesc_t& desc, int nVertices, int nIndices)
{
	MeshDesc_t* pThis = this;
	*pThis = desc;
	m_MaxVertices = nVertices;
	m_MaxIndices = nIndices;

	Assert(desc.m_CompressionType == VERTEX_COMPRESSION_NONE);
	if (desc.m_CompressionType != VERTEX_COMPRESSION_NONE)
	{
		Warning("Cannot use CBaseMeshReader with compressed vertices!\n");
	}
}

inline void CBaseMeshReader::Reset()
{
}




inline int CMeshReader::NumIndices() const
{
	return m_MaxIndices;
}

inline unsigned short CMeshReader::Index(int index) const
{
	Assert((index >= 0) && (index < m_MaxIndices));
	return m_pIndices[index * m_nIndexSize];
}

inline const Vector& CMeshReader::Position(int iVertex) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	return *(Vector*)((char*)m_pPosition + iVertex * m_VertexSize_Position);
}

inline unsigned int CMeshReader::Color(int iVertex) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	unsigned char* pColor = m_pColor + iVertex * m_VertexSize_Color;
	return (pColor[0] << 16) | (pColor[1] << 8) | (pColor[2]) | (pColor[3] << 24);
}

inline const float* CMeshReader::TexCoord(int iVertex, int iStage) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	return (float*)((char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage]);
}

inline void CMeshReader::TexCoord2f(int iVertex, int iStage, float& s, float& t) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	float* p = (float*)((char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage]);
	s = p[0];
	t = p[1];
}

inline const Vector2D& CMeshReader::TexCoordVector2D(int iVertex, int iStage) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	Vector2D* p = (Vector2D*)((char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage]);
	return *p;
}

inline float CMeshReader::Wrinkle(int iVertex) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	return *(float*)((char*)m_pWrinkle + iVertex * m_VertexSize_Wrinkle);
}

inline int CMeshReader::NumBoneWeights() const
{
	return m_NumBoneWeights;
}

inline const Vector& CMeshReader::Normal(int iVertex) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	return *(const Vector*)(const float*)((char*)m_pNormal + iVertex * m_VertexSize_Normal);
}

inline void CMeshReader::Normal(int iVertex, Vector& vNormal) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	const float* p = (const float*)((char*)m_pNormal + iVertex * m_VertexSize_Normal);
	vNormal.Init(p[0], p[1], p[2]);
}

inline const Vector& CMeshReader::TangentS(int iVertex) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	return *(const Vector*)((char*)m_pTangentS + iVertex * m_VertexSize_TangentS);
}

inline const Vector& CMeshReader::TangentT(int iVertex) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	return *(const Vector*)((char*)m_pTangentT + iVertex * m_VertexSize_TangentT);
}

inline float CMeshReader::BoneWeight(int iVertex) const
{
	Assert(iVertex >= 0 && iVertex < m_MaxVertices);
	float* p = (float*)((char*)m_pBoneWeight + iVertex * m_VertexSize_BoneWeight);
	return *p;
}

#endif  
