///////////////////////////////////////////////////////////////////////////////
/// @file deSurface.cpp
///
/// @brief Surface attributes for rendering geometry with
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date June 2003
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deSurface_priv.hpp"
#include "deBitmap.hpp"

static void TestTextureSet();

WorldObjectClassImp(deSurface);

static long s_SurfaceWOInterfaceID = -1;
deBoolean IdeSurface_Initialize()
{
	IdeWorldManager * manager = IdeWorld_GetManager();
	s_SurfaceWOInterfaceID = manager->GetUniqueWOInterfaceID();
	
	if (!WorldObjectRegister(deSurface))
		DEBUGRETURN(deFALSE, "IdeSurface_Initialize: WorldObjectRegister(deABTNode) failed");

	deTextureSet::Test();

	return deTRUE;
}

long IdeSurface_GetWOInterfaceID()
{
	return s_SurfaceWOInterfaceID;
}

IdeSurface* IdeSurface_CreateSurface()
{
	return new deSurface;
}

deSurface::deSurface()
:	m_RenderTexture(NULL),
	m_RenderMaterial(NULL)
{
}

deSurface::~deSurface()
{
	if (m_RenderTexture)
		m_RenderTexture->Release();
	if (m_RenderMaterial)
		m_RenderMaterial->Release();
}

void* deSurface::GetWOInterface(long interface_id)
{
	if (interface_id == s_SurfaceWOInterfaceID)
		return (IdeSurface*)(this);
	if (interface_id == 0)
		return (IdeWorldObject*)(this);
	return NULL;
}

struct deSurface_Header_t
{
	short VersionMajor;
	short VersionMinor;
};
static const deSurface_Header_t deSurface_Header = {0,1};
/*
Version 0.1:
December 24, 2003

struct deIDPair { u16 wid; u32 oid; }

u16			Version_Major
u16			Version_Minor
deIDPair	TextureID
deIDPair	MaterialID
*/
deBoolean deSurface::Serialize(IdeFile* File)
{
	deWorldID wid;
	deObjectID oid;

	// header information
	DE3D_STREAM_PUT(File, deSurface_Header.VersionMajor, u16);
	DE3D_STREAM_PUT(File, deSurface_Header.VersionMinor, u16);
	
	// texture set
	if (m_RenderTexture)
	{
		wid = m_RenderTexture->GetWorldID();
		oid = m_RenderTexture->GetObjectID();
	}
	else
	{
		wid = 0;
		oid = 0;
	}
	DE3D_STREAM_PUT(File, wid, u16);
	DE3D_STREAM_PUT(File, oid, u32);

	// material
	if (m_RenderMaterial)
	{
		wid = m_RenderMaterial->GetWorldID();
		oid = m_RenderMaterial->GetObjectID();
	}
	else
	{
		wid = 0;
		oid = 0;
	}
	DE3D_STREAM_PUT(File, wid, u16);
	DE3D_STREAM_PUT(File, oid, u32);

	return deTRUE;
}

deBoolean deSurface::DeSerialize(IdeFile* File, long DataLength)
{
	u32 AmtRead = 0;
	
	u16 VersionMajor, VersionMinor;
	// header information
	DE3D_STREAM_GET(File, VersionMajor, u16, AmtRead, DataLength);
	DE3D_STREAM_GET(File, VersionMinor, u16, AmtRead, DataLength);
	
	// check the version
	if (VersionMajor > deSurface_Header.VersionMajor ||
		(VersionMajor == deSurface_Header.VersionMajor && VersionMinor > deSurface_Header.VersionMinor))
		return deFALSE;
	
	if (VersionMajor == 0 && VersionMinor == 1)
	{
		deWorldID wid;
		deObjectID oid;
		
		// texture set
		DE3D_STREAM_GET(File, wid, u16, AmtRead, DataLength);
		DE3D_STREAM_GET(File, oid, u32, AmtRead, DataLength);
		m_TextureID.wid = wid;
		m_TextureID.oid = oid;
		
		// material
		DE3D_STREAM_GET(File, wid, u16, AmtRead, DataLength);
		DE3D_STREAM_GET(File, oid, u32, AmtRead, DataLength);
		m_MaterialID.wid = wid;
		m_MaterialID.oid = oid;
		return deTRUE;
	}

	return deFALSE;
}

deBoolean deSurface::DeSerializeLoad()
{
	IdeWorldObject* pObject;
	IdeRenderState* pState;
	IdeWorldManager* manager = IdeWorld_GetManager();
	m_RenderTexture = NULL;
	m_RenderMaterial = NULL;
	if (m_TextureID.wid)
	{
		pState = NULL;
		pObject = manager->GetWorldObject(m_TextureID.wid, m_TextureID.oid);
		if (pObject)
			pState = (IdeRenderState*)pObject->GetWOInterface(IdeRenderState_GetWOInterfaceID());
		else
			DEBUGMSG2("dePolyABT::DeSerializeLoad - failure loading object with WID=%d and OID=%d", m_TextureID.wid, m_TextureID.oid);
		if (pState)
		{
			m_RenderTexture = (IdeRenderTexture*)pState->GetRSInterface(IdeRenderState::TYPE_TEXTURE);
			if (m_RenderTexture)
			{
				m_RenderTexture->Claim();
			}
		}
	}
	if (m_MaterialID.wid)
	{
		pState = NULL;
		pObject = manager->GetWorldObject(m_MaterialID.wid, m_MaterialID.oid);
		if (pObject)
			pState = (IdeRenderState*)pObject->GetWOInterface(IdeRenderState_GetWOInterfaceID());
		else
			DEBUGMSG2("dePolyABT::DeSerializeLoad - failure loading object with WID=%d and OID=%d", m_MaterialID.wid, m_MaterialID.oid);
		if (pState)
		{
			m_RenderMaterial = (IdeRenderMaterial*)pState->GetRSInterface(IdeRenderState::TYPE_MATERIAL);
			if (m_RenderMaterial)
			{
				m_RenderMaterial->Claim();
			}
		}
	}
	
	return deTRUE;
}

void deSurface::SetRenderTexture(IdeRenderTexture* pRT)
{
	if (pRT)
		pRT->Claim();
	if (m_RenderTexture)
		m_RenderTexture->Release();
	m_RenderTexture = pRT;
}

void deSurface::SetRenderMaterial(IdeRenderMaterial* pRM)
{
	if (pRM)
		pRM->Claim();
	if (m_RenderMaterial)
		m_RenderMaterial->Release();
	m_RenderMaterial = pRM;
}

IdeRenderTexture* deSurface::GetRenderTexture()
{
	return m_RenderTexture;
}

IdeRenderMaterial* deSurface::GetRenderMaterial()
{
	return m_RenderMaterial;
}








deTextureSet::deTextureSet()
:	m_NameIndices(7)
{
	// nothing to see here...
}

deTextureSet::~deTextureSet()
{
	deReleasePtrsFunctor<IdeBitmapProxy> release_bmp_ptrs;
	deIter_App(m_Bitmaps.begin(), m_Bitmaps.end(), release_bmp_ptrs);
}

u32	deTextureSet::GetNumTextures() const
{
	return m_Bitmaps.size();
}

const char* deTextureSet::GetNameByIndex(s32 index) const
{
	if (index < 0 || index >= (s32)m_Bitmaps.size())
		DEBUGRETURN(NULL, "deTextureSet::GetNameByIndex - invalid index");
	return m_Names[index].const_str();
}

s32 deTextureSet::GetIndexByName(deString2 str) const
{
	s32* index = m_NameIndices.FindElement(str);
	if (index)
		return *index;
	return -1;
}

s32 deTextureSet::GetIndexByName(const char* name) const
{
	deString2 str(name);
	return GetIndexByName(str);
}

IdeBitmapProxy* deTextureSet::GetTextureByIndex(s32 index) const
{
	if (index < 0 || index >= (s32)m_Bitmaps.size())
		DEBUGRETURN(NULL, "deTextureSet::GetTextureByIndex - invalid index");
	return m_Bitmaps[index];
}

IdeBitmapProxy* deTextureSet::GetTextureByName(const char* name) const
{
	s32 index = GetIndexByName(name);
	if (index < 0)
		return NULL;
	return m_Bitmaps[index];
}

deBoolean deTextureSet::AddTextureName(deString2 str, s32* index)
{
	// can't add a name we already have
	if (GetIndexByName(str) >= 0)
		return deFALSE;
	// find where we'll be adding this one (always at the end)
	s32 length = m_Bitmaps.size();
	DE_ASSERT(length == (s32)m_Names.size() && length == (s32)m_NameIndices.Length());
	if (!m_Bitmaps.PushBack(NULL))
		goto end_failure;
	if (!m_Names.PushBack(str))
		goto end_failure;
	if (NULL == m_NameIndices.AddElement(length, str))
		goto end_failure;
	if (index)
		*index = length;
	return deTRUE;

end_failure:
	m_Bitmaps.Resize(length);
	m_Names.Resize(length);
	return deFALSE;
}

deBoolean deTextureSet::AddTextureName(const char* name, s32* index)
{
	deString2 str(name);
	return AddTextureName(str, index);
}

deBoolean deTextureSet::SetTextureByIndex(s32 index, IdeBitmapProxy* tex)
{
	if (index < 0 || index >= (s32)m_Bitmaps.size())
		DEBUGRETURN(deFALSE, "deTextureSet::SetTextureByIndex - invalid index");
	m_Bitmaps[index] = tex;
	return deTRUE;
}

deBoolean deTextureSet::SetTextureByName(const char* name, IdeBitmapProxy* tex)
{
	return SetTextureByIndex(GetIndexByName(name), tex);
}

IdeTextureSet* deTextureSet::CloneTextureSet(const char** names)
{
	if (!names)
		DEBUGRETURN(NULL, "deTextureSet::CloneTextureSet - invalid pointer");
	deTextureSet* newSet = new deTextureSet;
	if (!newSet)
		DEBUGRETURN(NULL, "deTextureSet::CloneTextureSet - could not allocate");
	deString2 str;
	int n;
	for (n = 0; names[n]; ++n)
	{
		s32 *idx1, idx2;
		str = names[n];
		idx1 = m_NameIndices.FindElement(str);
		if (idx1)
		{
			newSet->AddTextureName(str, &idx2);
			newSet->SetTextureByIndex(idx2, m_Bitmaps[*idx1]);
		}
	}
	return newSet;
}

void deTextureSet::Test()
{
	IdeTextureSet* texset = new deTextureSet;
	s32 idx;
	texset->AddTextureName("diffuse", &idx);
	texset->AddTextureName("detail", &idx);
	texset->AddTextureName("normal", &idx);
	texset->AddTextureName("gloss", &idx);

	const char* cloned[] = {"normal","detail","diffuse",0};
	IdeTextureSet* newSet = texset->CloneTextureSet(cloned);

	newSet->Release();
	texset->Release();
}
