/*==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==*/
#ifndef hsGMatState_inc
#define hsGMatState_inc

#include "hsColorRGBA.h"

class hsStream;

class hsGMatState {
public:
enum hsGMatBlendFlags {
	kBlendTest	= 0x1,							// dev
	// Rest of blends are mutually exclusive
	kBlendAlpha						= 0x2,		// dev
	kBlendMult						= 0x4,		// dev
	kBlendAdd						= 0x8,		// dev
	kBlendAddColorTimesAlpha		= 0x10,		// dev
	kBlendAntiAlias					= 0x20,
	kBlendDetail					= 0x40,
	kBlendNoColor					= 0x80,		// dev
	kBlendMADD						= 0x100,
	kBlendDot3						= 0x200,
	kBlendAddSigned					= 0x400,
	kBlendAddSigned2X				= 0x800,
	kBlendMask						= kBlendAlpha
									| kBlendMult
									| kBlendAdd
									| kBlendAddColorTimesAlpha
									| kBlendDetail
									| kBlendMADD
									| kBlendDot3
									| kBlendAddSigned
									| kBlendAddSigned2X,
	kBlendInvertAlpha				= 0x1000,	// dev
	kBlendInvertColor				= 0x2000,	// dev
	kBlendAlphaMult					= 0x4000,
	kBlendAlphaAdd					= 0x8000,
	kBlendNoVtxAlpha				= 0x10000,
	kBlendNoTexColor				= 0x20000,
	kBlendNoTexAlpha				= 0x40000,
	kBlendInvertVtxAlpha			= 0x80000,	// Invert ONLY the vertex alpha source
	kBlendAlphaAlways				= 0x100000,	// Alpha test always passes (even for alpha=0).
	kBlendInvertFinalColor			= 0x200000,
	kBlendInvertFinalAlpha			= 0x400000,
	kBlendEnvBumpNext				= 0x800000,
	kBlendSubtract					= 0x1000000,
	kBlendRevSubtract				= 0x2000000,
	kBlendAlphaTestHigh				= 0x4000000
};
enum hsGMatClampFlags {

	kClampTextureU	= 0x1,			// dev
	kClampTextureV	= 0x2,			// dev
	kClampTexture	= 0x3				// dev
};

enum hsGMatShadeFlags {

	kShadeSoftShadow		= 0x1,			// view, dev
	kShadeNoProjectors		= 0x2,			// projector
	kShadeEnvironMap		= 0x4,			// dev, load
	kShadeVertexShade		= 0x20,			// dev
	kShadeNoShade			= 0x40,			// view,dev
	kShadeBlack				= kShadeNoShade,
	kShadeSpecular			= 0x80,			// view, dev
	//kShadeNoFog				= 0x100,		// dev
	kShadeWhite				= 0x200,
	kShadeSpecularAlpha		= 0x400,
	kShadeSpecularColor		= 0x800,
	kShadeSpecularHighlight	= 0x1000,
	kShadeVertColShade		= 0x2000,
    kShadeInherit           = 0x4000,
	kShadeIgnoreVtxIllum	= 0x8000,
	kShadeEmissive			= 0x10000,		// Moved here 8.27 mcn. Only really sane to use with kMiscEndPassHere
	kShadeReallyNoFog		= 0x20000
};

enum hsGMatZFlags {
	kZIncLayer			= 0x1, // dev
	kZClearZ			= 0x4, // dev
	kZNoZRead			= 0x8, // dev
	kZNoZWrite			= 0x10,
	kZMask				= kZNoZWrite | kZClearZ | kZNoZRead,
	kZLODBias			= 0x20
};

enum hsGMatMiscFlags {
	kMiscWireFrame			= 0x1,			// dev (running out of bits)
	kMiscDrawMeshOutlines	= 0x2,			// dev, currently unimplemented
	kMiscTwoSided			= 0x4,			// view,dev
	kMiscDrawAsSplats		= 0x8,			// dev? bwt
	kMiscAdjustPlane		= 0x10,
	kMiscAdjustCylinder		= 0x20,
	kMiscAdjustSphere		= 0x40,
	kMiscAdjust				= kMiscAdjustPlane | kMiscAdjustCylinder| kMiscAdjustSphere,
    kMiscTroubledLoner      = 0x80,
	kMiscBindSkip			= 0x100,
	kMiscBindMask			= 0x200,
	kMiscBindNext			= 0x400,
	kMiscLightMap			= 0x800,
	kMiscUseReflectionXform	= 0x1000,		// Use the calculated reflection environment 
											// texture transform instead of layer->GetTransform()
	kMiscPerspProjection	= 0x2000,
	kMiscOrthoProjection	= 0x4000,
	kMiscProjection			= kMiscPerspProjection | kMiscOrthoProjection,

	kMiscRestartPassHere	= 0x8000,		// Tells pipeline to start a new pass beginning with this layer
											// Kinda like troubledLoner, but only cuts off lower layers, not 
											// higher ones (kMiscBindNext sometimes does this by implication)

	kMiscBumpLayer			= 0x10000,
	kMiscBumpDu				= 0x20000,
	kMiscBumpDv				= 0x40000,
	kMiscBumpDw				= 0x80000,
	kMiscBumpChans			= kMiscBumpDu | kMiscBumpDv | kMiscBumpDw,

	kMiscNoShadowAlpha		= 0x100000,
	kMiscUseRefractionXform	= 0x200000, // Use a refraction-like hack.
	kMiscCam2Screen         = 0x400000, // Expects tex coords to be XYZ in camera space. Does a cam to screen (not NDC) projection
										// and swaps Z with W, so that the texture projection can produce projected 2D screen coordinates.

	kAllMiscFlags			= 0xffffffff
};
enum StateIdx {
	kBlend,
	kClamp,
	kShade,
	kZ,
	kMisc 
};
	UInt32			fBlendFlags;
	UInt32			fClampFlags;
	UInt32			fShadeFlags;
	UInt32			fZFlags;
	UInt32			fMiscFlags;

	static hsBool Differs(UInt32 mine, UInt32 hers, UInt32 mask)
	{
		return (mine & mask) ^ (hers & mask);
	}

	static hsBool Differs(UInt32 mine, UInt32 hers)
	{
		return mine ^ hers;
	}

	hsBool operator!=(const hsGMatState& other)
	{
		return ((fBlendFlags ^ other.fBlendFlags)
			| (fClampFlags ^ other.fClampFlags)
			| (fShadeFlags ^ other.fShadeFlags)
			| (fZFlags ^ other.fZFlags)
			| (fMiscFlags ^ other.fMiscFlags));
	}
	UInt32 Value(int i) const
	{
		switch(i)
		{
		case kBlend:
			return fBlendFlags;
		case kClamp:
			return fClampFlags;
		case kShade:
			return fShadeFlags;
		case kZ:
			return fZFlags;
		case kMisc:
			return fMiscFlags;
		}
		hsAssert(false, "Bad param");
		return fBlendFlags;
	}
	UInt32& operator[](const int i)
	{
		switch(i)
		{
		case kBlend:
			return fBlendFlags;
		case kClamp:
			return fClampFlags;
		case kShade:
			return fShadeFlags;
		case kZ:
			return fZFlags;
		case kMisc:
			return fMiscFlags;
		}
		hsAssert(false, "Bad param");
		return fBlendFlags;
	}
	hsGMatState& operator|=(const hsGMatState& other)
	{
		fBlendFlags |= other.fBlendFlags;
		fClampFlags |= other.fClampFlags;
		fShadeFlags |= other.fShadeFlags;
		fZFlags |= other.fZFlags;
		fMiscFlags |= other.fMiscFlags;
		return *this;
	}
	hsGMatState& operator+=(const hsGMatState& other)
	{
		return operator|=(other);
	}
	hsGMatState& operator-=(const hsGMatState& other)
	{
		fBlendFlags &= ~other.fBlendFlags;
		fClampFlags &= ~other.fClampFlags;
		fShadeFlags &= ~other.fShadeFlags;
		fZFlags &= ~other.fZFlags;
		fMiscFlags &= ~other.fMiscFlags;
		return *this;
	}

	inline void Read(hsStream* s);
	inline void Write(hsStream* s);

	hsGMatState(UInt32 blend=0, UInt32 clamp=0, UInt32 shade=0, UInt32 z=0, UInt32 misc=0) 
		:	fBlendFlags(blend), 
			fClampFlags(clamp),
			fShadeFlags(shade),
			fZFlags(z),
			fMiscFlags(misc) {}
	void Reset() { fBlendFlags = fClampFlags = fShadeFlags = fZFlags = fMiscFlags = 0; }
	inline void Clear(const hsGMatState& state);
	inline void Composite(const hsGMatState& want, const hsGMatState& on, const hsGMatState& off);
};


#endif // hsGMatState_inc