/*==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 "plPhysicalProps.h"
#include "../plPhysical/plSimDefs.h"
// For plBoundsType
#include "../pnSceneObject/plSimulationInterface.h"

#include "plMaxNode.h"
#include "../MaxExport/plErrorMsg.h"

/** These enums are used to indicate which parameters are ignorable and can
	be overridden.
	We can't use the "PhysFlags," below, for this, because those only cover boolean properties.
	*** We should use the same enum for both and just not worry that some of the enums aren't
		usable as "ignore" flags, -- the readability & simplification gain would be worth it. */
enum CanIgnore
{
	kMemberGroup				= 0x1,
	kBounceGroup				= 0x2,
	kReportGroup				= 0x4,
	kMass						= 0x8,		
	kFriction					= 0x10,
	kRestitution				= 0x20,
	kBoundsType					= 0x40,
	kProxyNode					= 0x80,
	kPinned						= 0x100,
	kAlignToOwner				= 0x200,
	kCameraAvoid				= 0x400,
	kPhysAnim					= 0x800,
	kCanIgnoreLOSBlockCamera	= 0x1000,
	kCanIgnoreLOSBlockUI		= 0x2000,
	kCanIgnoreLOSUIItem			= 0x4000,
	kCanIgnoreLOSBlockCustom	= 0x8000,
	kCanIgnoreLOSShootable		= 0x10000,
	kCanIgnoreLOSAvatarWalkable	= 0x20000,
	kCanIgnoreLOSSwimRegion		= 0x40000,

	kAll						= 0xffffffff
};

enum PhysFlags
{
	kFlagPinned				= 0x1,
	kFlagAlignToOwner		= 0x2,
	kFlagCameraAvoid		= 0x4,
	kFlagPhysAnim			= 0x8,
	kFlagStartInactive		= 0x10,
	kFlagNoSynchronize		= 0x20,
	kFlagLOSBlockCamera		= 0x40,
	kFlagLOSBlockUI			= 0x80,
	kFlagLOSUIItem			= 0x100,
	kFlagLOSBlockCustom		= 0x200,
	kFlagLOSShootable		= 0x400,
	kFlagLOSAvatarWalkable	= 0x800,
	kFlagLOSSwimRegion		= 0x1000,
};

plPhysicalProps::plPhysicalProps() :
	fUsed(false),
	fCanIgnore(kAll),
	fGroup(0),
	fReportGroup(0),
	fMass(0),
	fFriction(0),
	fRestitution(0),
	fBoundsType(plSimDefs::kHullBounds),
	fProxyNode(nil),
	fFlags(0),
	fSubworld(nil),
	fNoSynchronize(0),
	fStartInactive(0),
	fAvAnimPushable(0)
{
}

bool plPhysicalProps::SetGroup(UInt32 group, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetParam(fGroup, group, kMemberGroup, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetReportGroup(UInt32 notifyGroup, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetParam(fReportGroup, notifyGroup, kReportGroup, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetMass(float mass, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	if(mass != 0.0f)
	{
		if (!GetPinned())
			node->SetMovable(true);
		node->SetForceLocal(true);
	}
	return ISetParam(fMass, mass, kMass, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetFriction(float friction, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetParam(fFriction, friction, kFriction, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetRestitution(float restitution, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetParam(fRestitution, restitution, kRestitution, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetBoundsType(int boundsType, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	hsAssert(boundsType >= 1 && boundsType < plSimDefs::kNumBounds, "Bad bounds type");
	return ISetParam(fBoundsType, boundsType, kBoundsType, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetProxyNode(plMaxNode *proxyNode, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	if( proxyNode )
		proxyNode->SetDrawable(false);

	return ISetParam(fProxyNode, proxyNode, kProxyNode, canIgnore, node, errMsg);
}

bool plPhysicalProps::IGetFlagParam(int flagType)
{
	return ((fFlags & flagType) != 0);
}

bool plPhysicalProps::ISetFlagParam(bool val, int flagType, int type, bool canIgnore, plMaxNode *node, plErrorMsg *errMsg)
{
	bool ourVal = IGetFlagParam(flagType);
	if (ISetParam(ourVal, val, type, canIgnore, node, errMsg))
	{
		if (ourVal)
			fFlags |= flagType;
		else
			fFlags &= ~flagType;
		return true;
	}
	
	return false;
}

bool plPhysicalProps::SetPinned(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagPinned, kPinned, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetLOSBlockCamera(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagLOSBlockCamera, kCanIgnoreLOSBlockCamera, canIgnore, node, errMsg);
}

bool plPhysicalProps::GetLOSBlockCamera()
{
	return IGetFlagParam(kFlagLOSBlockCamera);
}

bool plPhysicalProps::SetLOSBlockUI(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagLOSBlockUI, kCanIgnoreLOSBlockUI, canIgnore, node, errMsg);
}

bool plPhysicalProps::GetLOSBlockUI()
{
	return IGetFlagParam(kFlagLOSBlockUI);
}

bool plPhysicalProps::SetLOSUIItem(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagLOSUIItem, kCanIgnoreLOSUIItem, canIgnore, node, errMsg); 
}

bool plPhysicalProps::GetLOSUIItem()
{
	return IGetFlagParam(kFlagLOSUIItem);
}

bool plPhysicalProps::SetLOSBlockCustom(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagLOSBlockCustom, kCanIgnoreLOSBlockCustom, canIgnore, node, errMsg);
}

bool plPhysicalProps::GetLOSBlockCustom()
{
	return IGetFlagParam(kFlagLOSBlockCustom);
}

bool plPhysicalProps::SetCameraAvoidFlag(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(allowLOS, kFlagCameraAvoid, kCameraAvoid, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetLOSShootable(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagLOSShootable, kCanIgnoreLOSShootable, canIgnore, node, errMsg);
}
bool plPhysicalProps::GetLOSShootable()
{
	return IGetFlagParam(kFlagLOSShootable);
}

bool plPhysicalProps::SetLOSAvatarWalkable(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagLOSAvatarWalkable, kCanIgnoreLOSAvatarWalkable, canIgnore, node, errMsg);
}

bool plPhysicalProps::GetLOSAvatarWalkable()
{
	return IGetFlagParam(kFlagLOSAvatarWalkable);
}

bool plPhysicalProps::SetLOSSwimRegion(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(status, kFlagLOSSwimRegion, kCanIgnoreLOSSwimRegion, canIgnore, node, errMsg);
}

bool plPhysicalProps::GetLOSSwimRegion()
{
	return IGetFlagParam(kFlagLOSSwimRegion);
}

//bool plPhysicalProps::SetUILOSFlag(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
//{
//	return ISetFlagParam(allowLOS, kFlagUILOS, kUILOS, canIgnore, node, errMsg);
//}

bool plPhysicalProps::SetAlignToOwner(bool alignToOwner, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(alignToOwner, kFlagAlignToOwner, kAlignToOwner, canIgnore, node, errMsg);
}

bool plPhysicalProps::SetSubworld(plMaxNode* subworld)
{
	// Note that we do *not* set fUsed, because we don't (necessarily) want a physical for this node.
	// We just want to remember that it is a subworld.
	fSubworld = subworld;
	return true;
}

bool plPhysicalProps::SetPhysAnim(bool anim, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore)
{
	return ISetFlagParam(anim, kFlagPhysAnim, kPhysAnim, canIgnore, node, errMsg);
}

bool plPhysicalProps::GetPinned()
{
	return IGetFlagParam(kFlagPinned);
}

bool plPhysicalProps::GetCameraAvoid()
{
	return IGetFlagParam(kFlagCameraAvoid);
}

bool plPhysicalProps::GetAlignToOwner()
{
	return IGetFlagParam(kFlagAlignToOwner);
}

bool plPhysicalProps::GetPhysAnim()
{
	return IGetFlagParam(kFlagPhysAnim);
}

void plPhysicalProps::SetCanIgnore(UInt32 type, bool canIgnore)
{
	if (canIgnore)
		fCanIgnore |= type;
	else
		fCanIgnore &= ~type;
}

bool plPhysicalProps::CanIgnore(UInt32 type)
{
	return ((fCanIgnore & type) != 0);
}

void plPhysicalProps::IDisplayErrorMsg(plMaxNode *node, plErrorMsg *errMsg)
{
	if (!errMsg->IsBogus())
	{
		errMsg->Set(true,
			"Physics Conflict",
			"The node \"%s\" has a conflict in its physical settings.\nMake sure the physical components on it are compatible.",
			node->GetName()).Show();
	}
}