//	Tank.h - Class representing a player tank.
//  ----------------------------------------------------------------------------
//	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/>.
//	----------------------------------------------------------------------------

#ifndef TANK_H_
#define TANK_H_

#include "InterpolatedValue.h"
#include "GamePlayState.h"
#include "ThreeFloats.h"
#include "c0xHeaders.h"
#include "Collidable.h"
#include "Tweener.h"

#include <vector>

class TankCollisionModifier;
class SecondaryModifier;
class PrimaryDispenser;
class Drawer;

///	Class representing a player tank.
class Tank : public Collidable
{
  public:
	///	Constructor.
	Tank(const TwoFloats& pos, const ThreeFloats& col, int ind);
	///	Destructor.
	~Tank();

	///	Moves the tank.
	void updatePlayerInput(GamePlayState& state,
						   const TwoFloats& arenaTopLeft,
						   const TwoFloats& arenaBottomRight);
	///	Updates any of the tank's counters etc.
	void updateCounters(GamePlayState& state,
						const TwoFloats& arenaTopLeft,
						const TwoFloats& arenaBottomRight);
	///	Called at the start of a turn in turn-based mode.
	void startTurn();
	///	Draws the tank tracks.
	void drawTracks(shared_ptr<Drawer>& d, float timestepInterp);
	///	Draws the tank.
	void draw(shared_ptr<Drawer>& d, float timestepInterp);

	///	Called when a key down event relevant to this tank is received.
	void keyDown(const std::wstring& action);
	///	Called when a key up event relevant to this tank is received.
	void keyUp(const std::wstring& action);
	///	Called when a key or joystick move event is received for this tank.
	void move(const std::wstring& action, float value);

	///	Returns the tank's angle.
	float getAngle() const {return angle;};
	///	Returns the tank's colour.
	const ThreeFloats& getColour() const {return colour;};
	///	Returns the tank's armour level (0->1).
	float getArmour() const {return armour;};
	///	Returns the tank's secondary charge level.
	float getSecondaryCharge() const {return (1.0f-(secondaryCount/secondaryMax));};
	///	Returns true if the tank's been removed from the arena.
	bool getDead() const {return dead;};
	///	Returns true if the tank is currently receiving input from the player.
	bool getPlayerInput() const;

	///	Sets the tank's position.
	/*!
		Will reset the tank's tail by default.
	 */
	void setPosition(const TwoFloats& pos, bool resetTail = true);
	///	Called from GamePlayState when a tank has been killed.
	void setDead();

	///	Sets the tank's primary dispenser.
	void addPrimaryDispenser(shared_ptr<PrimaryDispenser>& dispenser);
	///	Adds a collision modifier to the tank.
	void addCollisionModifier(shared_ptr<TankCollisionModifier>& mod);
	///	Adds a SecondaryModifer.
	void addSecondaryTrigger(shared_ptr<SecondaryModifier> trigger);

	///	Increases the tank's primary fire rate.
	void increasePrimaryRate();
	///	Increases the tank's secondary fire rate.
	void increaseSecondaryRate();
	///	Turns on armour regeneration.
	void setArmourRegen();
	///	Switches off recoil from collisions.
	void setNoRecoil();
	///	Increases the tank's speed.
	void increaseSpeed();
	///	Doubles the tank's size.
	void setGiant();
	///	Inverts damage relationship (bullets heal; no bullets = armour decreases over time).
	void setBulletsHeal();
	///	Turns on diarrhea mines.
	void setDiarrheaMines();
	///	Turns on kamikaze mode.
	void setKamikaze();
	///	Multiplies the tank's global speed by the passed-in value.
	/*!
		This is used to speed up/slow down tanks when moving through speed/slow
		field puddles. It is reset at the end of each update() call.
	 */
	void multiplyGlobalSpeed(float val);
	///	Increments/decrements the tank's armour.
	void incrementHealth(float val);
	///	Temporarily increases the tank's speed a lot.
	void setTurbo();

	///	Updates the tank's bounce vector.
	void updateBounceVector(const TwoFloats& bounce);

	///	Returns true if the tank is set to have no recoil.
	bool getNoRecoil() const {return noRecoil;};

	///	Returns this tank's actual name string.
	const std::wstring& getActualName() const {return actualName;};

	///	Returns true if this entity wants to know when it collides with the named entity type.
	bool interestedInCollisionsWith(const std::wstring& entity) const;
	///	Called when this entity collides with another.
	bool collide(const shared_ptr<Collidable>& other,
				 GamePlayState& state);

	///	Returns the entity's position.
	const TwoFloats& getPosition() const {return (const TwoFloats&)position;};
	///	Returns the entity's radius.
	float getRadius() const {return radius;};
	///	Returns true if other entities can pass through this one.
	bool getIncorporeal() const {return false;};

	///	Test.
	bool operator==(const Tank& other) const;
  private:
	///	The length of the tank's trail.
	enum
	{
		TrailSize = 64
	};

	///	The tank's position.
	TwoFloats position;
	///	The tank's radius.
	float radius;
	///	The tank's colour.
	ThreeFloats colour;
	///	The tank's armour.
	float armour;

	///	The tank's current angle.
	float angle;
	///	The tank's current speed.
	float speed;
	///	The tank's maximum possible speed.
	float speedMax;
	///	The tank's global speed (for speed/slow puddles).
	float globalSpeed;
	///	Used to temporarily speed the tank by a lot.
	int turbo;

	///	The tank's back image.
	std::wstring backImage;
	///	The tank's line image.
	std::wstring lineImage;

	///	The tank's actual name.
	std::wstring actualName;
	///	The tank's index in GamePlayState's tanks vector.
	int index;

	///	Used to fire a primary shot.
	std::vector<shared_ptr<PrimaryDispenser> > primaryDispensers;
	///	Any modifiers for when the tank hits something.
	std::vector<shared_ptr<TankCollisionModifier> > collisionModifiers;
	///	Any modifiers for when the the player hits the secondary button.
	std::vector<shared_ptr<SecondaryModifier> > secondaryTriggers;

	///	Used to regenerate the tank's armour.
	float regen;
	///	If true, the tank will not recoil from being hit by bullets/other tanks.
	bool noRecoil;
	///	If true, bullets heal the tank; without them its armour decreases over time.
	bool bulletsHeal;
	///	If true, the player has no control over their secondary fire.
	bool diarrheaMines;
	///	If true, the player will explode on contact with other tanks.
	bool kamikaze;
	///	Useed to avoid the kamikaze triggering multiple shockwaves.
	int kamikazeCount;

	///	Used to bounce the tank away when it collides with another tank.
	TwoFloats bounceVector;
	///	Used to make the tank recoil when it fires a primary shot.
	TwoFloats recoilVector;

	///	The speed we want the tank to be moving at.
	float desiredSpeed;
	///	Used to increment/decrement angle when the relevant key's held.
	float angleIncrement;
	///	The amount to increment/decrement angle by (this is less when the tank is giant).
	float angleAmount;

	///	True if the tank is currently firing its primary gun.
	bool firingPrimary;
	///	Used to limit the speed the tank fires its primary gun.
	float primaryCount;
	///	The amount of time between primary shots.
	float primaryMax;
	///	True if the tank is currently firing its secondary gun.
	bool firingSecondary;
	///	Used to limit the speed the tank fires its secondary gun.
	float secondaryCount;
	///	The amount of time between secondary shots.
	float secondaryMax;

	///	Used to shoot out a burst of particles behind the tank when it primary fires.
	ParticleGenerator particles;

	///	True if the player's left key is held down.
	bool leftIsDown;
	///	True if the player's right key is held down.
	bool rightIsDown;

	///	True if the tank is dead and has been removed from the arena.
	bool dead;

	///	Used to draw the tank's trail.
	int trailCount;
	///	Used to draw the tank's trail.
	std::vector<TwoFloats> trailPos;
	///	Used to draw the tank's trail.
	std::vector<float> trailSize;
	///	Used to draw the tank's trail.
	std::vector<ThreeFloats> trailCol;
	///	Used to draw the tank's trail.
	std::vector<float> trailAlpha;
};

#endif
