
#include <assert.h>
#include <GlobalDevice.h>
#include <iostream>

//---------------------------------------------------------------------

#include <GameObject.h>
#include <Player.h>

//---------------------------------------------------------------------

GameObject::GameObject(int parId, const vector3df& pos): id(parId), 
	isTextNodeVisible(false), is3DMesh(false), isCollidable(true), customIdData(NULL), textNode(NULL), balloon(NULL), stupidEndId(-1), shadow(NULL)
{
	const char* srcFile = "";
	std::string strTextNode;
	bool hasAlphaChannel = true;
	bool needDynamicShadows = false;
	switch(id)
	{
	case GAMEOBJECT_GIRLBODY:
		srcFile = "data/girl.3ds";
		strTextNode = "Need urgent medical care";
		is3DMesh = true;
		hasAlphaChannel = true;
		break;
	case GAMEOBJECT_BATTERY:
		srcFile = "data/batteries.png";
		strTextNode = "Batteries";
		is3DMesh = false;
		hasAlphaChannel = true;
		isCollidable = false;
		break;
	case GAMEOBJECT_MEDICALROBOT:
		srcFile = "data/medicalrobot.3ds";
		strTextNode = "Medical robot, need batteries";
		is3DMesh = true;
		hasAlphaChannel = false;
		needDynamicShadows = true;
		customIdData = new u32[2];
		*customIdData = 0;
		customIdData[1] = 0;
		break;
	case GAMEOBJECT_CHEMICALLIQUID:
		srcFile = "data/chemical.3ds";
		strTextNode = "Chemical liquid";
		is3DMesh = true;
		hasAlphaChannel = true;
		isCollidable = false;
		break;
	case GAMEOBJECT_PAPER:
		srcFile = "data/paper.3ds";
		strTextNode = "";
		is3DMesh = true;
		hasAlphaChannel = true;
		isCollidable = false;
		customIdData = new u32[1];
		*customIdData = 0;
		break;
	case GAMEOBJECT_EXPLOSION:
		srcFile = "data/explosion.png";
		strTextNode = "";
		is3DMesh = false;
		hasAlphaChannel = true;
		isCollidable = false;
		customIdData = new u32[1];
		*customIdData = GlobalDevice::getDevice()->getTimer()->getRealTime();
		break;
	case GAMEOBJECT_CHEMICALBOTTLE:
		srcFile = "data/chemicalbottle.png";
		strTextNode = "";
		is3DMesh = false;
		hasAlphaChannel = true;
		isCollidable = true;
		break;
	case GAMEOBJECT_FIREFIGHTERROBOT:
		srcFile = "data/firefighterrobot.3ds";
		strTextNode = "\"ALERT! ALERT!\" ";
		is3DMesh = true;
		hasAlphaChannel = false;
		isCollidable = true;
		needDynamicShadows = true;
		customIdData = new u32[2];
		*customIdData = 0;
		customIdData[1] = 0;
		break;
	case GAMEOBJECT_GIRLBLOOD:
		srcFile = "data/blood.3ds";
		strTextNode = "Blood";
		is3DMesh = true;
		hasAlphaChannel = true;
		isCollidable = false;
		break;
	case GAMEOBJECT_SCREWDRIVER:
		srcFile = "data/screwdriver.png";
		strTextNode = "Screwdriver";
		is3DMesh = false;
		hasAlphaChannel = true;
		isCollidable = false;
		break;
	default: assert(false);
	}
	
	//--------------------------
	// load mesh or texture

	if (is3DMesh)
	{
		if (needDynamicShadows)
		{
			IAnimatedMeshSceneNode* meshnode = GlobalDevice::loadAnimatedNodeFromMeshFile(srcFile, hasAlphaChannel);
			shadow = meshnode->addShadowVolumeSceneNode(0, -1, false);
			objectNode = meshnode;
		}
		else
			objectNode = GlobalDevice::loadNodeFromMeshFile(srcFile, hasAlphaChannel);
		objectNode->setPosition(pos);
	}
	else
	{
		dimension2d<s32> textureOriginalSize;
		objectNode = GlobalDevice::loadBillboardNodeFromTexture(srcFile, 8.f, textureOriginalSize, hasAlphaChannel);
		objectNode->setPosition(pos+vector3df(0.f, textureOriginalSize.Height / 8.f/2,0.f));
		//objectNode->setDebugDataVisible(irr::scene::EDS_BBOX);
		//objectNode->setMaterialFlag(video::EMF_ZBUFFER, false);
		//objectNode->setVisible(false);
		//textNode->setVisible(false);
		//if (id == GAMEOBJECT_EXPLOSION)
		//	((IBillboardSceneNode*)objectNode)->setSize(dimension2df(0.1f, 0.1f));
	}
	
	//--------------------------
	//3D text scene node

	if (strTextNode.size() != 0)
	{
		textNode = GlobalDevice::getSceneManager()->addTextSceneNode(
			GlobalDevice::getGUIEnvironment()->getSkin()->getFont(), L"");
		assert(textNode);
		//textNode->setSize(dimension2df(strTextNode.size()*1.f, 2.f));
		textNode->setMaterialFlag(video::EMF_ZBUFFER, false);
		textNode->setTextColor(SColor(255,255,255,255));
		textNode->setPosition(objectNode->getPosition() + vector3df(0.f, objectNode->getBoundingBox().MaxEdge.Y + 10.f,0.f));

		dimension2d<s32> textureOriginalSize;
		balloon = GlobalDevice::loadBillboardNodeFromTexture("data/balloon.png", 8.f, textureOriginalSize, true);
		balloon->setPosition(textNode->getPosition()+vector3df(0.f, -1.f, 1.f));
		balloon->setMaterialFlag(video::EMF_ZBUFFER, false);

		setTextNodeText(strTextNode);
	}
}

//---------------------------------------------------------------------

void GameObject::manage(Player* player, std::vector<GameObject*>& gameObjects)
{
	const vector3df& playerPosition = player->getPosition();
	const vector3df& thispos = objectNode->getPosition();
	const float period = GlobalDevice::getFrameDuration();

	if (GlobalDevice::getEventManager()->keys[KEY_F2] && shadow != NULL) {shadow->setVisible(false);}
	if (GlobalDevice::getEventManager()->keys[KEY_F3] && shadow != NULL) {shadow->setVisible(true);}
	
	if (textNode != NULL && (id != GAMEOBJECT_GIRLBODY && id != GAMEOBJECT_MEDICALROBOT && id != GAMEOBJECT_BATTERY && id != GAMEOBJECT_FIREFIGHTERROBOT))
	{
		if (playerPosition.getDistanceFrom(thispos) < objectNode->getBoundingBox().getExtent().getLength() * 1.5f)
		{
			textNode->setVisible(true);
			balloon->setVisible(true);
		}
		else
		{
			textNode->setVisible(false);
			balloon->setVisible(false);
		}
	}
	
	u32 currentTime = GlobalDevice::getDevice()->getTimer()->getRealTime();
	switch(id)
	{
	case GAMEOBJECT_MEDICALROBOT:
		if (*customIdData == 0 && player->nearbyBatteries != NULL && player->nearbyBatteries->getPosition().getDistanceFrom(thispos) < 20.f)
		{
			player->nearbyBatteries->autoDestroy();
			player->nearbyBatteries = NULL;
			*customIdData = 1;
			setTextNodeText("\"Energy found, moving now.\"");
		}
		else if (*customIdData == 1)
		{
			setPosition(getPosition() + vector3df(-1.f * period, 0.f, 0.f));
			if (getPosition().X < -90.f)
			{
				*customIdData = 2;
				setTextNodeText("\"Huh oh. Bad direction.\"");
				customIdData[1] = GlobalDevice::getDevice()->getTimer()->getRealTime();
			}
		}
		else if (*customIdData >= 2 && *customIdData <= 7)
		{
			if (currentTime > customIdData[1] + 5000)
			{
				switch(*customIdData)
				{
				case 2:setTextNodeText("\"Maybe I had to go to the right.\"");break;
				case 3:setTextNodeText("\"I'm not a very smart robot, you know.\"");break;
				case 4:setTextNodeText("\"Maybe I am bugged.\"");break;
				case 5:setTextNodeText("\"Is this alarming to have a bug when I am a medical robot?\"");break;
				case 6:setTextNodeText("\"This is worrying me someway.\"");break;
				case 7:
					setTextNodeText("*sigh*");
					stupidEndId = 0;
					break;
				}
				customIdData[1] = GlobalDevice::getDevice()->getTimer()->getRealTime();
				(*customIdData)++;
			}
		}
	case GAMEOBJECT_CHEMICALLIQUID:
		if (player->nearbyBatteries != NULL && player->nearbyBatteries->getPosition().getDistanceFrom(thispos) < 10.f)
		{
			GameObject* explosion = new GameObject(GameObject::GAMEOBJECT_EXPLOSION, this->getPosition());
			GameObject* firefighterrobot = new GameObject(GameObject::GAMEOBJECT_FIREFIGHTERROBOT, vector3df(65.f,0.f,-45.f));
			pendingCreatedObjects.push_back(explosion);
			pendingCreatedObjects.push_back(firefighterrobot);
			player->nearbyBatteries->autoDestroy();
			player->nearbyBatteries = NULL;
			//autoDestroy();
		}
		break;
	case GAMEOBJECT_BATTERY:
		if (playerPosition.getDistanceFrom(thispos) < 7.f && !this->isAutodestroyed())
		{
			float angleRadians = (-player->getRotation().Y -90.f +180.f) * 3.14159265f/180.f;
			float period = GlobalDevice::getFrameDuration();
			setPosition(getPosition() + vector3df(cos(angleRadians) * period * 100.f, 0.f, sin(angleRadians) * period * 100.f));
			player->nearbyBatteries = this;
		}
		else
			player->nearbyBatteries = NULL;
		break;
	case GAMEOBJECT_PAPER:
		if (playerPosition.getDistanceFrom(thispos) < 30.f)
			*customIdData = 1;
		if (*customIdData == 1 && getPosition().Y > 10.f)
		{
			setPosition(getPosition() + vector3df(0.f, -1.f*period, 0.f));
			objectNode->setRotation(objectNode->getRotation()+vector3df(10.f*period,0.f, 0.f));
		}
		break;
	case GAMEOBJECT_EXPLOSION:
		if (currentTime > *customIdData + 500)
		{
			//one second has passed
			autoDestroy();
		}
		//((IBillboardSceneNode*)objectNode)->setSize(((IBillboardSceneNode*)objectNode)->getSize() + dimension2df(period * 100.f, period * 100.f));
		break;
	case GAMEOBJECT_FIREFIGHTERROBOT:
		if (*customIdData == 0)
		{
			if (getPosition().Z < 25.f)
				setPosition(getPosition() + vector3df(0.f, 0.f, 6.f * period));
			else
			{
				*customIdData = 1;
				setTextNodeText("\"Huh oh. Nothing to do.\"");
				customIdData[1] = GlobalDevice::getDevice()->getTimer()->getRealTime();
			}
		}
		else if (*customIdData >= 1 && *customIdData <= 6)
		{
			if (currentTime > customIdData[1] + 5000)
			{
				switch(*customIdData)
				{
				case 1:setTextNodeText("\"There is no fire.\"");break;
				case 2:setTextNodeText("\"I was expecting a fire, because of the combustion.\"");break;
				case 3:setTextNodeText("\"This is pretty disappointing.\"");break;
				case 4:setTextNodeText("\"What can I do now?\"");break;
				case 5:setTextNodeText("\"I knew it, I'm useless in this world...\"");break;
				case 6:
					setTextNodeText("*cry*");
					stupidEndId = 1;
				}
				customIdData[1] = GlobalDevice::getDevice()->getTimer()->getRealTime();
				(*customIdData)++;
			}
		}
		break;
	case GAMEOBJECT_SCREWDRIVER:
		if (playerPosition.getDistanceFrom(thispos) < 7.f && !this->isAutodestroyed())
		{
			float angleRadians = (-player->getRotation().Y -90.f +180.f) * 3.14159265f/180.f;
			float period = GlobalDevice::getFrameDuration();
			setPosition(getPosition() + vector3df(cos(angleRadians) * period * 100.f, 0.f, sin(angleRadians) * period * 100.f));
		}
		break;
	}
}

//---------------------------------------------------------------------

bool GameObject::needShakingScreen()
{
	if (id == GAMEOBJECT_EXPLOSION)
		return true;
	return false;
}

//---------------------------------------------------------------------

bool GameObject::isCollidingPos(aabbox3df box, const vector3df& pos) const //const aabbox3d<f32>& box) const
{
	if (!isCollidable) return false;
	if (is3DMesh)
	{
		box.MinEdge.X += pos.X;
		box.MinEdge.Y += pos.Y;
		box.MinEdge.Z += pos.Z;
		box.MaxEdge.X += pos.X;
		box.MaxEdge.Y += pos.Y;
		box.MaxEdge.Z += pos.Z;
		return objectNode->getTransformedBoundingBox().intersectsWithBox(box);
	}
	else
	{
		IBillboardSceneNode* billboard = reinterpret_cast<IBillboardSceneNode*>(objectNode);
		return 
			(pos.X+5.f >= billboard->getPosition().X - billboard->getSize().Width/2)
			&& (pos.X-5.f <= billboard->getPosition().X + billboard->getSize().Width/2)
			&& (pos.Z+5.f >= billboard->getPosition().Z - billboard->getSize().Height/2)
			&& (pos.Z-5.f <= billboard->getPosition().Z + billboard->getSize().Height/2);
	}
}

//---------------------------------------------------------------------

GameObject::~GameObject()
{
	objectNode->remove();
	if (textNode != NULL) textNode->remove();
	if (balloon != NULL) balloon->remove();
	if (customIdData != NULL) delete [] customIdData;
}

//---------------------------------------------------------------------

void GameObject::setPosition(const vector3df& pos)
{
	vector3df relativePosText, relativePosTextBalloon;
	if(textNode != NULL) 
	{
		relativePosText = textNode->getPosition() - objectNode->getPosition();
		relativePosTextBalloon = balloon->getPosition() - objectNode->getPosition();
	}
	objectNode->setPosition(pos);
	if(textNode != NULL) 
	{
		textNode->setPosition(pos + relativePosText);
		balloon->setPosition(pos + relativePosTextBalloon);
	}
}

//---------------------------------------------------------------------

void GameObject::setTextNodeText(const std::string& text)
{
	core::stringw strTextNodeW = L"";
	strTextNodeW += core::stringw(text.c_str());
	textNode->setText(strTextNodeW.c_str());
	balloon->setSize(dimension2df(1.5f *text.size() * 1024.f / GlobalDevice::getMainWindowSize().X, balloon->getSize().Height));
}

//---------------------------------------------------------------------
