#include "shape.h"

#include "sprite.h"
#include "terrain.h"

#include <Octree.h>

#include <math.h>

using namespace std;
using namespace stdext;
using namespace kLibrary;
using namespace cbk;

namespace CBK
{
	DWORD Shape::_maxNumLights = 0;

	Shape::Shape(Mesh* mesh, IDirect3DDevice9* device, Octree<Shape*>* octree) : Object(device)
	{
		D3DXMATRIX rotation;
		D3DXMatrixIdentity(&rotation);
		D3DXMatrixIdentity(&_parentTransform);

		_parent = 0;

		_mesh = mesh;

		setPosition(0.0f, 0.0f, 0.0f);
		scale(1.0f, 1.0f, 1.0f);
		setVelocity(0.0f, 0.0f, 0.0f);
		setAcceleration(0.0f, 0.0f, 0.0f);
		setRotation(rotation);

		_materialLoader = 0;
		_textureLoader = 0;
		_meshLoader = 0;

		_bMovingTo = false;
		_maxVelocity = 100.0f;
		_maxAcceleration = 100.0f;

		if (_mesh != 0)
			_mesh->calcBoundaries();
		_terrain = 0;

		_selCircle = new Shapes::Sprite(MeshParam(device, 0, _textureLoader), "SelectionCircle.png");

		_octreeNode = 0;
		_octree = octree;
	}

	Shape::Shape(IDirect3DDevice9* device, Octree<Shape*>* octree) : Object(device)
	{
		D3DXMATRIX rotation;
		D3DXMatrixIdentity(&rotation);
		D3DXMatrixIdentity(&_parentTransform);

		_parent = 0;
		_mesh = 0;

		setPosition(0.0f, 0.0f, 0.0f);
		scale(1.0f, 1.0f, 1.0f);
		setVelocity(0.0f, 0.0f, 0.0f);
		setAcceleration(0.0f, 0.0f, 0.0f);
		setRotation(rotation);

		_materialLoader = 0;
		_textureLoader = 0;
		_meshLoader = 0;

		_bMovingTo = false;
		_maxVelocity = 100.0f;
		_maxAcceleration = 10.0f;

		_terrain = 0;

		_selCircle = new Shapes::Sprite(MeshParam(device, 0, _textureLoader), "SelectionCircle.png");

		_octreeNode = 0;
		_octree = octree;
	}

	Shape::~Shape(void)
	{
		if (this->_meshLoader != 0)
		{
			if (_mesh != 0)
				this->_meshLoader->unload(dynamic_cast<Shapes::xLoader*>(_mesh));
		}
		else
		{
			delete _mesh;
		}

		if (_selCircle != 0)
			delete _selCircle;

		if (_octreeNode != 0)
			if (_octree != 0)
				_octree->Delete(_octreeNode);
	}

	Object& Shape::reset(void)
	{
		if (_mesh != 0)
			_mesh->reset();

		return *this;
	}

	const DWORD& Shape::getMaxNumLights(void)
	{
		return _maxNumLights;
	}

	Shape& Shape::setTerrainYOffset(const float& yOffset)
	{
		if (_mesh != 0)
			_mesh->setTerrainYOffset(yOffset);
		return *this;
	}

	const float Shape::getTerrainYOffset(void) const
	{
		if (_mesh != 0)
			return _mesh->getTerrainYOffset();
		else
			return 0xffffffff;
	}

	Shape& Shape::setMeshLoader(kLibrary::Factory<Shapes::xLoader, const MeshParam&>* meshLoader)
	{
		if (_meshLoader != 0)
			if (_mesh != 0)
				_meshLoader->unload(dynamic_cast<Shapes::xLoader*>(_mesh));

		_meshLoader = meshLoader;

		return *this;
	}

	Object& Shape::load(const std::string& name)
	{
		if (_meshLoader != 0)
		{
			if (_mesh != 0)
				_meshLoader->unload(dynamic_cast<Shapes::xLoader*>(_mesh));

			_mesh = _meshLoader->load(name, MeshParam(device, _materialLoader, _textureLoader));
		}
		else if (_mesh != 0)
			_mesh->load(name, device);

		if (_mesh != 0)
			_mesh->calcBoundaries();

		return *this;
	}

	const D3DXVECTOR3& Shape::getRealPosition(void) const
	{
		updateTransform();

		return _realPosition;
	}

	Shape& Shape::stopMoveTo(void)
	{
		_bMovingTo = false;
		return *this;
	}

	const D3DXVECTOR3* const Shape::getMovingTo(void) const
	{
		if (_bMovingTo)
			return &_vectMoveTo;
		else
			return 0;
	}

	Shape& Shape::calcBoundaries(void)
	{
		if (_mesh != 0)
			_mesh->calcBoundaries();

		return *this;
	}

	const float Shape::getCollideSphereRadius(void) const
	{
		if (_mesh != 0)
			return _mesh->getCollisionSphereRadius() * getMaxScale();
		else
			return -1;
	}

	const BoundingBox* const Shape::getBoundingBox(void) const
	{
		if (_mesh != 0)
			return &(_mesh->getBoundingBox());
		else
			return 0;
	}

	const float& Shape::getMaxScale(void) const
	{
		return max(max(_vectScale.x, _vectScale.y), _vectScale.z);
	}

	const FLOAT Shape::getSpeed(void) const
	{
		return D3DXVec3Length(&_vectVelocity);
	}

	const FLOAT Shape::getSpeedXZ(void) const
	{
		D3DXVECTOR2 t = _vectVelocity;
		return D3DXVec2Length(&t);
	}

	const D3DXVECTOR3 Shape::getDirection(void) const
	{
		D3DXVECTOR3 vect;
		D3DXVec3Normalize(&vect, &_vectVelocity);
		return vect;
	}

	const D3DXVECTOR2 Shape::getDirectionXZ(void) const
	{
		D3DXVECTOR2 vect = _vectVelocity;
		D3DXVec2Normalize(&vect, &vect);
		return vect;
	}

	const FLOAT Shape::getAccelSpeed(void) const
	{
		return D3DXVec3Length(&_vectAcceleration);
	}

	const D3DXVECTOR3 Shape::getAccelDirection(void) const
	{
		D3DXVECTOR3 vect;
		D3DXVec3Normalize(&vect, &_vectAcceleration);
		return vect;
	}

	const FLOAT Shape::getAccelSpeedXZ(void) const
	{
		D3DXVECTOR2 t = _vectAcceleration;
		return D3DXVec2Length(&t);
	}

	const D3DXVECTOR2 Shape::getAccelDirectionXZ(void) const
	{
		D3DXVECTOR2 vect = _vectAcceleration;
		D3DXVec2Normalize(&vect, &vect);
		return vect;
	}

	float Shape::isCollideXZ(const Shape& other, const float& dt) const
	{
		const float distance = getDistance(&other);

		const float tSpeed = getSpeedXZ() * dt;
		const float oSpeed = other.getSpeedXZ() * dt;

		const D3DXVECTOR3 normal = other.getRealPosition() - getRealPosition();
		D3DXVECTOR2 normalXZ = D3DXVECTOR2(normal.x, normal.z);
		D3DXVec2Normalize(&normalXZ, &normalXZ);

		const D3DXVECTOR2 tVelocity = getDirectionXZ();
		const D3DXVECTOR2 oVelocity = other.getDirectionXZ();

//		const float tDot = D3DXVec2Dot(&normalXZ, &tVelocity);
//		const float oDot = D3DXVec2Dot(&normalXZ, &oVelocity);

		const float SphereRadius = other.getCollideSphereRadius() + getCollideSphereRadius();
		const float Speed = tSpeed + oSpeed;
		const float CollideDistance = SphereRadius + Speed;
		if (distance <= CollideDistance)
			if (distance < SphereRadius)
				return 0.0f;
			else
				return ((distance - SphereRadius) / Speed);

		return -1.0f;
	}

	float Shape::isCollide(const Shape& other, const float& dt) const
	{
		const float distance = getDistance(&other);

		const float tSpeed = getSpeed() * dt;
		const float oSpeed = other.getSpeed() * dt;

		D3DXVECTOR3 normal = other.getRealPosition() - getRealPosition();
		D3DXVec3Normalize(&normal, &normal);

		const D3DXVECTOR3 tVelocity = getDirection();
		const D3DXVECTOR3 oVelocity = other.getDirection();

		const float tDot = D3DXVec3Dot(&normal, &tVelocity);
		const float oDot = D3DXVec3Dot(&normal, &oVelocity);

		const float SphereRadius = other.getCollideSphereRadius() + getCollideSphereRadius();
		const float Speed = (tSpeed * tDot) + (oSpeed * oDot);
		const float CollideDistance = SphereRadius + Speed;
		if (distance <= CollideDistance)
			if (distance < SphereRadius)
				return 0.0f;
			else
				return ((distance - SphereRadius) / Speed);

		return -1.0f;
	}

	Shape& Shape::setCurrentTerrain(CBK::Shape* terrain)
	{
		_terrain = terrain;
		return *this;
	}

	Shape& Shape::setOctreeNode(cbk::Octree<Shape*>::oNode* octreeNode)
	{
		if (_octreeNode != 0)
			if (_octree != 0)
				_octree->Delete(_octreeNode);

		_octreeNode = octreeNode;

		return *this;
	}

	cbk::Octree<Shape*>::oNode* Shape::getOctreeNode(void) const
	{	
		return _octreeNode;
	}

	cbk::Octree<Shape*>* Shape::getOctree(void) const
	{
		return _octree;
	}

	Shape& Shape::setOctree(cbk::Octree<Shape*>* octree)
	{
		if (_octreeNode != 0)
			if (_octree != 0)
				_octree->Delete(_octreeNode);

		_octreeNode = 0;
		_octree = octree;
		return *this;
	}

	Shape& Shape::update(const float& dt)
	{
		D3DXVECTOR3 rPos = getRealPosition();
		float terrainHeight = rPos.y;

		if (_terrain != 0)
		{
			terrainHeight = dynamic_cast<Terrain*>(_terrain->getMesh())->getHeight(rPos.x, rPos.z) + getTerrainYOffset();
			D3DXVECTOR3 nOffset = D3DXVECTOR3(0.0f, terrainHeight - rPos.y, 0.0f);
			move(nOffset);
			rPos = getRealPosition();
		}

		if (_bMovingTo)
		{
			D3DXVECTOR3 diff = _vectMoveTo - rPos;
			D3DXVECTOR2 diffXZ = D3DXVECTOR2(diff.x, diff.z);

			if (D3DXVec2Length(&diffXZ) <= 1.0f)
			{
				setPosition(_vectMoveTo);
				_bMovingTo = false;
				setVelocity(0.0f, 0.0f, 0.0f);
			}
			else
			{
				D3DXVec2Normalize(&diffXZ, &diffXZ);
				diffXZ *= _maxVelocity;
				diff.x = diffXZ.x;
				diff.y = rPos.y - terrainHeight;
				diff.z = diffXZ.y;
				setVelocity(diff);
			}
		}

		if (_mesh != 0)
		{
			vector<const Octree<Shape*>::oNode*> col;
			const Vector tVelocity = (_vectVelocity * dt);
			const float steps = ceil((tVelocity / Vector(0.1f, 0.1f, 0.1f)).magnitude());
			const Vector dVelocity = (_vectVelocity * dt) / steps;

			rPos = getRealPosition();
			int found = 0;
			
			const BoundingBox mBB = _mesh->getBoundingBox();
			const float mMS = getMaxScale();

			{
				D3DXMATRIX i;
				device->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&i));
			}
			for (int x = 0; x < int(steps); x++)
			{
				rPos += dVelocity;
				BoundingBox bb = (mBB * mMS) + rPos;
				bb.draw(device);
				found += _octree->Search(&col, rPos, mBB, mMS);
			}
			if (found > 0)	//Collision
				for (vector<const Octree<Shape*>::oNode*>::iterator x = col.begin(); x != col.end(); x++)
				{
					if ((*x)->getData() == this)
						continue;
					float collideTime = isCollideXZ(*(*x)->getData(), dt);
					if (collideTime >= 0)
					{
						//http://www.gamasutra.com/features/20000208/lander_02.htm
						move(_vectVelocity * dt * collideTime);
						rPos = getRealPosition();
						if (_bMovingTo)
						{
							D3DXVECTOR3 diff = rPos - _vectMoveTo;
							if (D3DXVec3Length(&diff) <= (getCollideSphereRadius() * 5.0f))
							{
								setVelocity(0.0f, 0.0f, 0.0f);
								_bMovingTo = false;
								break;
							}
						}

						D3DXVECTOR3 hitNormal = (*x)->getData()->getRealPosition() - rPos;
						D3DXVECTOR3 velNormal;
						D3DXVec3Normalize(&velNormal, &_vectVelocity);
						D3DXVec3Normalize(&hitNormal, &hitNormal);
						D3DXVECTOR3 diff = (D3DXVec3Dot(&hitNormal, &velNormal) * hitNormal);
						diff = (velNormal - diff) - (0.9f * hitNormal);
						setVelocity(diff * D3DXVec3Length(&_vectVelocity));
						break;
					}
				}
		}

		if (getAccelSpeed() > _maxAcceleration)
			setAcceleration(getAccelDirection() * _maxAcceleration);

		accelerate(_vectAcceleration * dt);
		if (getSpeedXZ() > _maxVelocity)
			setVelocity(getDirection() * _maxVelocity);

		move(_vectVelocity * dt);

		return *this;
	}

	Shape& Shape::removeObject(Shape* object)
	{
		return *this;
	}

	const D3DXVECTOR3& Shape::getVelocity(void) const
	{
		return _vectVelocity;
	}

	const D3DXVECTOR3& Shape::getAcceleration(void) const
	{
		return _vectAcceleration;
	}

	const float Shape::getDistance(const Shape* other) const
	{
		const D3DXVECTOR3 ray = other->getRealPosition() - getRealPosition();
		return D3DXVec3Length(&ray);
	}

	const float Shape::getDistanceXZ(const Shape* other) const
	{
		const D3DXVECTOR3 ray = other->getRealPosition() - getRealPosition();
		const D3DXVECTOR2 rayXZ = D3DXVECTOR2(ray.x, ray.z);

		return D3DXVec2Length(&rayXZ);
	}

	void Shape::apply(void)
	{
		applyTransform();
		applyLighting();
		if (_mesh != 0)
			_mesh->apply();

		if (isSelected)
			if (_selCircle != 0)
				_selCircle->apply();
	}

	Shape& Shape::drawBoundingBox(void)
	{
		if (_mesh != 0)
		{
			applyTransform();
			_mesh->drawBoundingBox();
		}

		return *this;
	}

	void Shape::draw(const DWORD& subset)
	{
		applyTransform();
		applyLighting();
		if (_mesh != 0)
			_mesh->draw(subset);

		if (isSelected)
			if (_selCircle != 0)
				_selCircle->apply();
	}

	void Shape::updateTransform(void) const
	{
		bool wasUpdated = false;

		//Check to see if we need to update
		if (_needTransformUpdate)
		{
			D3DXMATRIX tempScale;
			D3DXMATRIX tempTranslation;

			//If so, calculate the scaling and translation matrices
			D3DXMatrixScaling(&tempScale, _vectScale.x, _vectScale.y, _vectScale.z);
			D3DXMatrixTranslation(&tempTranslation, _vectTranslation.x, _vectTranslation.y, _vectTranslation.z);

			//And combine them with the rotation matrix stored
			_transform = tempScale * _matrixRotation * tempTranslation;

			//Update our booleans
			_needTransformUpdate = false;
			wasUpdated = true;

			//Update our local rotation axis
			D3DXVECTOR4 tPoint = D3DXVECTOR4(1.0f, 0.0f, 0.0f, 0.0f);
			D3DXVec4Transform(&tPoint, &tPoint, &_transform);
			_tAxisXLocal = D3DXVECTOR3(tPoint);
			D3DXVec3Normalize(&_tAxisXLocal, &_tAxisXLocal);

			tPoint = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 0.0f);
			D3DXVec4Transform(&tPoint, &tPoint, &_transform);
			_tAxisYLocal = D3DXVECTOR3(tPoint);
			D3DXVec3Normalize(&_tAxisYLocal, &_tAxisYLocal);

			tPoint = D3DXVECTOR4(0.0f, 0.0f, 1.0f, 0.0f);
			D3DXVec4Transform(&tPoint, &tPoint, &_transform);
			_tAxisZLocal = D3DXVECTOR3(tPoint);
			D3DXVec3Normalize(&_tAxisZLocal, &_tAxisZLocal);
		}

		//Check to see if we have a parent
		D3DXMATRIX tempParent;
		if (_parent != 0)
		{
			//If we do, get its transform (includes its parent's transform, if any)
			tempParent = _parent->getTransform();
		}
		else
		{
			//If not, we use an identity as the parent's transform
			D3DXMatrixIdentity(&tempParent);
		}

		//See if it has changed seince our last check
		if (_parentTransform != tempParent)
		{
			//if so, save it as our current parent transform
			_parentTransform = tempParent;
			wasUpdated = true;
		}

		//If our transform was updated,
		if (wasUpdated)
		{
			//Combine the parent transform with the location transform
			_completeTransform = _transform * _parentTransform;

			//and calculate our real position (our position including both our and our parent's transform)
			D3DXVECTOR4 tPoint(0.0f, 0.0f, 0.0f, 1.0f);
			D3DXVec4Transform(&tPoint, &tPoint, &_completeTransform);
			_realPosition = D3DXVECTOR3(tPoint);

			tPoint = D3DXVECTOR4(1.0f, 0.0f, 0.0f, 0.0f);
			D3DXVec4Transform(&tPoint, &tPoint, &_completeTransform);
			_tAxisXWorld = D3DXVECTOR3(tPoint);
			D3DXVec3Normalize(&_tAxisXWorld, &_tAxisXWorld);

			tPoint = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 0.0f);
			D3DXVec4Transform(&tPoint, &tPoint, &_completeTransform);
			_tAxisYWorld = D3DXVECTOR3(tPoint);
			D3DXVec3Normalize(&_tAxisYWorld, &_tAxisYWorld);

			tPoint = D3DXVECTOR4(0.0f, 0.0f, 1.0f, 0.0f);
			D3DXVec4Transform(&tPoint, &tPoint, &_completeTransform);
			_tAxisZWorld = D3DXVECTOR3(tPoint);
			D3DXVec3Normalize(&_tAxisZWorld, &_tAxisZWorld);

			if (_selCircle != 0)
				_selCircle->setTransform(_completeTransform);
		}
	}

	Shape& Shape::setPosition(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		if ((_vectTranslation.x != x) || (_vectTranslation.y != y) || (_vectTranslation.z != z))
		{
			_vectTranslation.x = x;
			_vectTranslation.y = y;
			_vectTranslation.z = z;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::setPosition(const D3DXVECTOR3& position)
	{
		if (_vectTranslation != position)
		{
			_vectTranslation = position;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::move(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		if ((0 != x) || (0 != y) || (0 != z))
		{
			_vectTranslation.x += x;
			_vectTranslation.y += y;
			_vectTranslation.z += z;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::move(const D3DXVECTOR3& offset)
	{
		if ((0 != offset.x) || (0 != offset.y) || (0 != offset.z))
		{
			_vectTranslation += offset;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::moveTo(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		_vectMoveTo.x = x;
		if (_terrain != 0)
			_vectMoveTo.y = y;
		else
			_vectMoveTo.y = dynamic_cast<Terrain*>(_terrain->getMesh())->getHeight(_vectMoveTo.x, _vectMoveTo.z) + 10.0f;
		_vectMoveTo.z = z;
		_bMovingTo = true;

		return *this;
	}

	Shape& Shape::moveTo(const D3DXVECTOR3& position)
	{
		_vectMoveTo = position;
		if (_terrain != 0)
			_vectMoveTo.y = dynamic_cast<Terrain*>(_terrain->getMesh())->getHeight(_vectMoveTo.x, _vectMoveTo.z) + 10.0f;
		_bMovingTo = true;

		return *this;
	}

	Shape& Shape::setVelocity(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		_vectVelocity = D3DXVECTOR3(x, y, z);
		return *this;
	}

	Shape& Shape::setVelocity(const D3DXVECTOR3& velocity)
	{
		_vectVelocity = velocity;
		return *this;
	}

	Shape& Shape::accelerate(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		_vectVelocity += D3DXVECTOR3(x, y, z);
		return *this;
	}

	Shape& Shape::accelerate(const D3DXVECTOR3& acceleration)
	{
		_vectVelocity += acceleration;
		return *this;
	}

	Shape& Shape::setMaxVelocity(const float& maxVelocity)
	{
		if (_maxVelocity >= 0)
			this->_maxVelocity = _maxVelocity;
		else
			this->_maxVelocity = 0;

		return *this;
	}

	const float& Shape::getMaxVelocity(void) const
	{
		return _maxVelocity;
	}

	Shape& Shape::setAcceleration(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		_vectAcceleration = D3DXVECTOR3(x, y, z);
		return *this;
	}

	Shape& Shape::setAcceleration(const D3DXVECTOR3& acceleration)
	{
		_vectAcceleration = acceleration;
		return *this;
	}

	Shape& Shape::modifyAcceleration(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		_vectAcceleration += D3DXVECTOR3(x, y, z);
		return *this;
	}

	Shape& Shape::modifyAcceleration(const D3DXVECTOR3& deltaAcceleration)
	{
		_vectAcceleration += deltaAcceleration;
		return *this;
	}

	Shape& Shape::setMaxAcceleration(const float& maxAcceleration)
	{
		if (maxAcceleration >= 0)
			_maxAcceleration = maxAcceleration;
		else
			_maxAcceleration = 0;

		return *this;
	}

	const float& Shape::getMaxAcceleration(void) const
	{
		return _maxAcceleration;
	}

	Shape& Shape::setRotation(const D3DXMATRIX& rotation)
	{
		if (_matrixRotation != rotation)
		{
			_matrixRotation = rotation;
			_needTransformUpdate = true;
		}
	
		return *this;
	}

	Shape& Shape::rotate(const D3DXMATRIX& rotation)
	{
		_matrixRotation *= rotation;

		_needTransformUpdate = true;

		return *this;
	}

	Shape& Shape::setRotation(const D3DXVECTOR3& rotation)
	{
		D3DXMATRIX rotate;

		D3DXMatrixRotationY(&_matrixRotation, rotation.x);
		D3DXMatrixRotationX(&rotate, rotation.y);
		_matrixRotation *= rotate;
		D3DXMatrixRotationZ(&rotate, rotation.z);
		_matrixRotation *= rotate;

		_needTransformUpdate = true;

		return *this;
	}

	Shape& Shape::setRotation(const FLOAT& Yaw, const FLOAT& Pitch, const FLOAT& Roll)
	{
		D3DXMATRIX rotate;

		D3DXMatrixRotationY(&_matrixRotation, Yaw);
		D3DXMatrixRotationX(&rotate, Pitch);
		_matrixRotation *= rotate;
		D3DXMatrixRotationZ(&rotate, Roll);
		_matrixRotation *= rotate;

		_needTransformUpdate = true;

		return *this;
	}

	Shape& Shape::rotate(const D3DXVECTOR3& rotation)
	{
		if ((0 != rotation.x) || (0 != rotation.y) || (0 != rotation.z))
		{
			D3DXMATRIX rotate;

			D3DXMatrixRotationY(&rotate, rotation.x);
			_matrixRotation *= rotate;
			D3DXMatrixRotationX(&rotate, rotation.y);
			_matrixRotation *= rotate;
			D3DXMatrixRotationZ(&rotate, rotation.z);
			_matrixRotation *= rotate;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::rotate(const FLOAT& Yaw, const FLOAT& Pitch, const FLOAT& Roll)
	{
		if ((0 != Yaw) || (0 != Pitch) || (0 != Roll))
		{
			D3DXMATRIX rotate;

			D3DXMatrixRotationY(&rotate, Yaw);
			_matrixRotation *= rotate;
			D3DXMatrixRotationX(&rotate, Pitch);
			_matrixRotation *= rotate;
			D3DXMatrixRotationZ(&rotate, Roll);
			_matrixRotation *= rotate;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::setMesh(Mesh* mesh)
	{
		if (_mesh != 0)
			if (_meshLoader != 0)
				_meshLoader->unload(dynamic_cast<Shapes::xLoader*>(_mesh));

		_mesh = mesh;
		if (_mesh != 0)
		{
			setTextureLoader(_mesh->getTextureLoader());
			setMaterialLoader(_mesh->getMaterialLoader());
			_mesh->calcBoundaries();
		}

		return *this;
	}

	Shape& Shape::scale(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		if ((_vectScale.x != x) || (_vectScale.y != y) || (_vectScale.z != z))
		{
			_vectScale.x = x;
			_vectScale.y = y;
			_vectScale.z = z;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::scale(const D3DXVECTOR3& scale)
	{
		if (_vectScale != scale)
		{
			_vectScale = scale;

			_needTransformUpdate = true;
		}

		return *this;
	}

	Shape& Shape::adjustScale(const FLOAT& x, const FLOAT& y, const FLOAT& z)
	{
		if ((0 != x) || (0 != y) || (0 != z))
		{
			_vectScale.x += x;
			_vectScale.y += y;
			_vectScale.z += z;

			_needTransformUpdate = true;
		}
		
		return *this;
	}

	Shape& Shape::adjustScale(const D3DXVECTOR3& difference)
	{
		if ((0 != difference.x) || (0 != difference.y) || (0 != difference.z))
		{
			_vectScale += difference;

			_needTransformUpdate = true;
		}
		
		return *this;
	}

	void Shape::applyTransform(void) const
	{
		updateTransform();

		if (device != 0)
			device->SetTransform(D3DTS_WORLD, &_completeTransform);
	}

	const D3DXMATRIX& Shape::getTransform(void) const
	{
		updateTransform();
		return _completeTransform;
	}

	const D3DXMATRIX& Shape::getRotation(void) const
	{
		return _matrixRotation;
	}

	const D3DXVECTOR3& Shape::getScale(void) const
	{
		return _vectScale;
	}

	const D3DXVECTOR3& Shape::getTranslation(void) const
	{
		return _vectTranslation;
	}

	Shape& Shape::setParent(Shape* parent)
	{
		if (_parent != 0)
			_parent->removeChild(this);

		_parent = parent;
		if (_parent != 0)
			_parent->addChild(this);

		return *this;
	}

	Shape* Shape::getParent(void) const
	{
		return _parent;
	}

	Shape& Shape::setMaterialLoader(Factory<Material, IDirect3DDevice9*>* materialLoader)
	{
		_materialLoader = materialLoader;
		if (_mesh != 0)
			_mesh->setMaterialLoader(_materialLoader);

		return *this;
	}

	kLibrary::Factory<Material, IDirect3DDevice9*>* Shape::getMaterialLoader(void) const
	{
		return _materialLoader;
	}

	Shape& Shape::setMaterial(const string& name, const size_t& subset)
	{
		if (_mesh != 0)
			_mesh->setMaterial(name, subset);

		return *this;
	}

	const Material* Shape::getMaterial(const size_t& subset) const
	{
		if (_mesh != 0)
			return _mesh->getMaterial(subset);
		else
			return 0;
	}

	const size_t Shape::getSubsetCount(void) const
	{
		if (_mesh != 0)
			return _mesh->getSubsetCount();
		else
			return 0;
	}

	kLibrary::Factory<Texture, IDirect3DDevice9*>* Shape::getTextureLoader(void) const
	{
		return _textureLoader;
	}

	Shape& Shape::setTextureLoader(Factory<Texture, IDirect3DDevice9*>* textureLoader)
	{
		_textureLoader = textureLoader;
		if (_mesh != 0)
			_mesh->setTextureLoader(_textureLoader);

		_selCircle->setTextureLoader(_textureLoader);
		_selCircle->setImage("SelectionCircle.png");

		return *this;
	}

	Shape& Shape::setTexture(const string& name, const size_t& subset)
	{
		if (_mesh != 0)
			_mesh->setTexture(name, subset);

		return *this;
	}

	const Texture* Shape::getTexture(const size_t& subset) const
	{
		if (_mesh != 0)
			return _mesh->getTexture(subset);
		else
			return 0;
	}

	Shape& Shape::addLight(Light* light)
	{
		if (!isLightLinked(light))
			_lightsLinked.push_back(light);

		if (_lightsLinked.size() > _maxNumLights)
			_maxNumLights = DWORD(_lightsLinked.size());

		return *this;
	}

	Mesh* Shape::getMesh(void) const
	{
		return _mesh;
	}

	Shape& Shape::removeLight(Light* light)
	{
		_lightsLinked.remove(light);
		return *this;
	}

	bool Shape::isLightLinked(Light* light)
	{
		list<Light*>::iterator currentLight = _lightsLinked.begin();
		list<Light*>::iterator end = _lightsLinked.end();

		for (; currentLight != end; currentLight++)
			if (*currentLight == light)
				return true;

		return false;
	}

	const Shape& Shape::applyLighting(void) const
	{
		list<Light*>::const_iterator currentLight = _lightsLinked.begin();
		list<Light*>::const_iterator end = _lightsLinked.end();
		DWORD x = 0;

		for (; currentLight != end; currentLight++)
		{
			(*currentLight)->apply(x);
			x++;
		}
		for (; x < _maxNumLights; x++)
			Light::disable(device, x);

		return *this;
	}

	Shape& Shape::addChild(Shape* child)
	{
		list<Shape*>::iterator curChild = getFirstChild();
		list<Shape*>::iterator end = getChildEnd();

		for (; curChild != end; curChild++)
			if (*curChild == child)
				return *this;

		_children.push_back(child);
		return *this;
	}

	Shape& Shape::removeChild(Shape* child)
	{
		list<Shape*>::iterator curChild = getFirstChild();
		list<Shape*>::iterator end = getChildEnd();

		for (; curChild != end; curChild++)
			if (*curChild == child)
				_children.erase(curChild);

		return *this;
	}

	list<Shape*>::iterator Shape::getFirstChild(void)
	{
		return _children.begin();
	}

	list<Shape*>::iterator Shape::getChildEnd(void)
	{
		return _children.end();
	}
}