/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "HeadSpin.h"
#include "plMtlCollector.h"

#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h"

#include "../MaxPlasmaMtls/Materials/plCompositeMtl.h"
#include "../MaxPlasmaMtls/Materials/plDecalMtl.h"
#include "../MaxPlasmaMtls/Materials/plMultipassMtl.h"
#include "../MaxPlasmaMtls/Materials/plParticleMtl.h"
#include "../MaxPlasmaMtls/Materials/plPassMtl.h"
#include "../MaxPlasmaMtls/Materials/plClothingMtl.h"

#include "../MaxComponent/plGUIComponents.h"
#include "../MaxComponent/pfGUISkinComp.h"
#include "../MaxComponent/plMiscComponents.h"

#include "../MaxMain/plMaxNodeBase.h"

static bool IsPlasmaMtl(Mtl *mtl)
{
	if (mtl->ClassID() == COMP_MTL_CLASS_ID ||
		mtl->ClassID() == DECAL_MTL_CLASS_ID ||
		mtl->ClassID() == MULTIMTL_CLASS_ID ||
		mtl->ClassID() == PARTICLE_MTL_CLASS_ID ||
		mtl->ClassID() == PASS_MTL_CLASS_ID ||
		mtl->ClassID() == CLOTHING_MTL_CLASS_ID)
		return true;
	return false;
}

static bool IsTexmapOK(Texmap *tex, UInt8 flags)
{
	if (flags & plMtlCollector::kPlasmaOnly && !plPlasmaMAXLayer::GetPlasmaMAXLayer(tex))
		return false;

	return true;
}

static bool IsMtlOK(Mtl *mtl, UInt8 flags)
{
	if (flags & plMtlCollector::kPlasmaOnly && !IsPlasmaMtl(mtl))
		return false;

	if (flags & plMtlCollector::kNoMultiMtl && mtl->ClassID() == MULTIMTL_CLASS_ID)
		return false;

	if (flags & plMtlCollector::kClothingMtlOnly && mtl->ClassID() != CLOTHING_MTL_CLASS_ID)
		return false;

	return true;
}

void GetMtlsRecur(MtlBase *mtlBase, MtlSet* mtls, TexSet* texmaps, UInt32 flags)
{
	if (!mtlBase)
		return;

	if (mtlBase->SuperClassID() == TEXMAP_CLASS_ID)
	{
		Texmap* tex = (Texmap*)mtlBase;
		if (texmaps && IsTexmapOK(tex, flags))
			texmaps->insert(tex);
	}
	else if(mtlBase->SuperClassID() == MATERIAL_CLASS_ID)
	{
		Mtl* mtl = (Mtl*)mtlBase;

		if (mtls && IsMtlOK(mtl, flags))
			mtls->insert(mtl);

		// Get the bitmaps from all the textures this material contains
		int i;
		int numTex = mtl->NumSubTexmaps();
		for (i = 0; i < numTex; i++)
		{
			Texmap *tex = mtl->GetSubTexmap(i);
			if (tex)
			{
				if (texmaps && IsTexmapOK(tex, flags))
					texmaps->insert(tex);
			}
		}

		// Do the same for any submtls
		if (!(flags & plMtlCollector::kNoSubMtls))
		{
			int numMtl = mtl->NumSubMtls();
			for (i = 0; i < numMtl; i++)
				GetMtlsRecur(mtl->GetSubMtl(i), mtls, texmaps, flags);
		}
	}
	else
	{
		hsAssert(0, "What kind of material is this?");
	}
}

static void GetTexmapPBs(Texmap* tex, PBSet& pbs)
{
	if (!tex)
		return;

	plPlasmaMAXLayer *plasma = plPlasmaMAXLayer::GetPlasmaMAXLayer( tex );
	if( plasma != nil )
	{
		int i;
		for( i = 0; i < plasma->GetNumBitmaps(); i++ )
		{
			PBBitmap *pbbm = plasma->GetPBBitmap( i );
			if( pbbm != nil )
				pbs.insert( pbbm );
		}
	}
	else
	{
		for (int i = 0; i < tex->NumRefs(); i++)
		{
			ReferenceTarget* r = tex->GetReference(i);
			if (r && r->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID)
			{
				IParamBlock2* pb = (IParamBlock2*)r;
				for (int j = 0; j < pb->NumParams(); j++)
				{
					if (pb->GetParameterType(pb->IndextoID(j)) == TYPE_BITMAP)
					{
						PBBitmap *pbbm = pb->GetBitmap(j);
						
						if (pbbm)
							pbs.insert(pbbm);
					}
				}
			}
		}
	}
}

#include "../MaxPlasmaLights/plRealTimeLightBase.h"

static void GetNodeMtlsRecur(INode *node, MtlSet* mtls, TexSet* texmaps, UInt32 flags)
{
	Mtl *mtl = node->GetMtl();
	GetMtlsRecur(mtl, mtls, texmaps, flags);

	Object* obj = node->GetObjectRef();
	if (obj && (obj->ClassID() == RTSPOT_LIGHT_CLASSID || obj->ClassID() == RTPDIR_LIGHT_CLASSID))
	{
		Texmap* texmap = ((plRTLightBase*)obj)->GetProjMap();
		GetMtlsRecur(texmap, mtls, texmaps, flags);
	}

	plGUIControlBase *gui = plGUIControlBase::GetGUIComp( node );
	if( gui != nil )
	{
		UInt32 i;
		for( i = 0; i < gui->GetNumMtls(); i++ )
			GetMtlsRecur( gui->GetMtl( i ), mtls, texmaps, flags );
	}
	else
	{
		// Skins aren't controls
		plGUISkinComp *guiSkin = plGUISkinComp::GetGUIComp( node );
		if( guiSkin != nil )
		{
			UInt32 i;
			for( i = 0; i < guiSkin->GetNumMtls(); i++ )
				GetMtlsRecur( guiSkin->GetMtl( i ), mtls, texmaps, flags );
		}
		else
		{
			// Um, other components
			plComponentBase *base = ( ( plMaxNodeBase *)node )->ConvertToComponent();
			if( base != nil )
			{
				if( base->ClassID() == IMAGE_LIB_CID )
				{
					pfImageLibComponent *iLib = (pfImageLibComponent *)base;
					UInt32 i;
					for( i = 0; i < iLib->GetNumBitmaps(); i++ )
						GetMtlsRecur( iLib->GetBitmap( i ), mtls, texmaps, flags );
				}
			}
		}
	}

	for (int i = 0; i < node->NumberOfChildren(); i++)
		GetNodeMtlsRecur(node->GetChildNode(i), mtls, texmaps, flags);
}

static void GetEditorMtls(MtlSet* mtls, TexSet* texmaps, UInt32 flags)
{
	static const int kNumEditorSlots = 24;

	Interface *ip = GetCOREInterface();
	for (int i = 0; i < kNumEditorSlots; i++)
	{
		MtlBase *mtlBase = ip->GetMtlSlot(i);
		GetMtlsRecur(mtlBase, mtls, texmaps, flags);
	}
}

void plMtlCollector::GetMtls(MtlSet* mtls, TexSet* texmaps, UInt32 flags)
{
	Interface *ip = GetCOREInterface();

	// Make a list of all the textures from the GetSceneMtls() func
	MtlBaseLib* sceneMtls = ip->GetSceneMtls();
	for(int i = 0; i < sceneMtls->Count(); i++)
	{
		GetMtlsRecur((*sceneMtls)[i], mtls, texmaps, flags);
	}

	// Add any more we find traversing the node hierarchy
	INode *root = ip->GetRootNode();
	GetNodeMtlsRecur(root, mtls, texmaps, flags);

	if (!(flags & kUsedOnly))
		GetEditorMtls(mtls, texmaps, flags);
}

void plMtlCollector::GetMtlLayers(Mtl *mtl, LayerSet& layers)
{
	TexSet tex;
	GetMtlsRecur(mtl, nil, &tex, kPlasmaOnly);

	TexSet::iterator it = tex.begin();
	for (; it != tex.end(); it++)
	{
		layers.insert((plPlasmaMAXLayer*)*it);
	}
}

void plMtlCollector::GetAllTextures(TexNameSet& texNames)
{
	TexSet tex;
	GetMtls(nil, &tex);

	PBSet pbs;
	TexSet::iterator it = tex.begin();
	for (; it != tex.end(); it++)
		GetTexmapPBs(*it, pbs);

	PBSet::iterator pbIt = pbs.begin();
	for (; pbIt != pbs.end(); pbIt++)
	{
		PBBitmap* pbbm = *pbIt;
		texNames.insert(pbbm->bi.Name());
	}
}