//	Bullet.cpp - Basic bullet class.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	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/>.
//	----------------------------------------------------------------------------

#include "BulletModifiers.h"
#include "drawing/Drawer.h"
#include "GamePlayState.h"
#include "MiscHelper.h"
#include "Puddle.h"
#include "Bullet.h"

//------------------------------------------------------------------------------
Bullet::Bullet(const TwoFloats& pos,
			   const TwoFloats& dir,
			   const ThreeFloats& col,
			   const std::wstring& owner):
Collidable(L"Bullet"),
position(pos),
direction(dir),
parent(owner),
globalSpeed(1.0f),
colour(col),
dead(false),
noDeathAction(false),
lifespan(0.0f),
invinciCount(0),
collidedWithZombie(false)
{
	int i;

	for(i=0;i<TrailSize;++i)
	{
		trailPos.push_back(position);
		trailSize.push_back(fakeLog((float)(i)/(float)TrailSize) * 6.0f);
		trailCol.push_back(colour);
		trailAlpha.push_back((float)(i)/(float)TrailSize);
		/*if(i < (TrailSize/2))
			trailAlpha.push_back(((float)(i*2)/(float)TrailSize) * 0.125f);
		else
			trailAlpha.push_back((1.0f-((float)((i-(TrailSize/2))*2)/(float)TrailSize)) * 0.125f);*/
	}
}

//------------------------------------------------------------------------------
Bullet::~Bullet()
{

}

//------------------------------------------------------------------------------
void Bullet::update(const TwoFloats& arenaTopLeft,
					const TwoFloats& arenaBottomRight,
					GamePlayState& state)
{
	unsigned int i;

	actualDir = direction;

	for(i=0;i<directionModifiers.size();++i)
		directionModifiers[i]->alterDirection(actualDir, *this, state);

	position += actualDir * 6.0f * globalSpeed;

	trailPos.erase(trailPos.begin());
	trailPos.push_back(position);

	if((position.x-5.0f) < arenaTopLeft.x)
		dead = true;
	else if((position.x+5.0f) > arenaBottomRight.x)
		dead = true;

	if((position.y-5.0f) < arenaTopLeft.y)
		dead = true;
	else if((position.y+5.0f) > arenaBottomRight.y)
		dead = true;

	if(invinciCount)
		--invinciCount;

	lifespan += 1.0f;

	//Reset global speed.
	globalSpeed = 1.0f;
}

//------------------------------------------------------------------------------
void Bullet::drawTrail(shared_ptr<Drawer>& d, float timestepInterp)
{
	d->drawConnectedLine(L"",
						 trailPos,
						 trailSize,
						 trailCol,
						 trailAlpha);
}

//------------------------------------------------------------------------------
void Bullet::draw(shared_ptr<Drawer>& d, float timestepInterp)
{
	TwoFloats origin;
	TwoFloats tempPos;
	float angle = -origin.getAngle(actualDir);

	tempPos = trailPos[TrailSize-3];
	tempPos.x -= 6.0f;
	tempPos.y -= 10.0f;
	d->drawRotImage(L"Bullet",
					tempPos,
					angle,
					TwoFloats(6.0f, 10.0f),
					1.0f,
					colour*1.33f);
}

//------------------------------------------------------------------------------
void Bullet::deathAction(GamePlayState& state)
{
	unsigned int i;

	if(!noDeathAction)
	{
		for(i=0;i<deathModifiers.size();++i)
			deathModifiers[i]->bulletDeath(*this, state);
	}
}

//------------------------------------------------------------------------------
void Bullet::resurrect()
{
	dead = false;
	invinciCount = 5;
}

//------------------------------------------------------------------------------
void Bullet::setDead()
{
	dead = true;
	noDeathAction = true;
}

//------------------------------------------------------------------------------
void Bullet::setPosition(const TwoFloats& pos)
{
	position = pos;
}

//------------------------------------------------------------------------------
void Bullet::setDirection(const TwoFloats& dir)
{
	direction = dir;
}

//------------------------------------------------------------------------------
void Bullet::multiplyGlobalSpeed(float val)
{
	globalSpeed *= val;
}

//------------------------------------------------------------------------------
void Bullet::addCollisionModifier(shared_ptr<BulletCollisionModifier>& mod)
{
	collisionModifiers.push_back(mod);
}

//------------------------------------------------------------------------------
void Bullet::addDeathModifier(shared_ptr<BulletDeathModifier>& mod)
{
	deathModifiers.push_back(mod);
}

//------------------------------------------------------------------------------
void Bullet::addDirectionModifier(shared_ptr<BulletDirectionModifier>& mod)
{
	directionModifiers.push_back(mod);
}

//------------------------------------------------------------------------------
bool Bullet::interestedInCollisionsWith(const std::wstring& entity) const
{
	if(entity == L"Bullet")
		return false;
	else
		return (invinciCount == 0);
}

//------------------------------------------------------------------------------
bool Bullet::collide(const shared_ptr<Collidable>& other,
					 GamePlayState& state)
{
	unsigned int i;
	const std::wstring& n(other->getName());

	if(!invinciCount)
		dead = true;

	if(n == L"Wall")
	{
		for(i=0;i<collisionModifiers.size();++i)
			collisionModifiers[i]->wallCollide(*this, state, other);
	}
	else if(n == L"Shockwave")
	{
		float offset;
		const TwoFloats origin;
		TwoFloats diff(other->getPosition() - position);
		float dist = origin.getDistance(diff);
		float rad = getRadius() + other->getRadius();

		diff = diff.normal();
		offset = (rad - dist);
		position -= diff * offset;

		dead = false;
	}
	else if(n == L"Puddle")
	{
		shared_ptr<Puddle> puddle = dynamic_pointer_cast<Puddle>(other);

		dead = false;

		if(puddle)
			puddle->collideBullet(*this, state);
	}
	else if(n == L"Zombie")
		collidedWithZombie = true;

	return false;
}
