/* bzflag
 * Copyright (c) 1993-2017 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef __SHOTMANAGER_H__
#define __SHOTMANAGER_H__

#include "common.h"

/* common interface headers */
#include "global.h"  /* for TeamColor */
#include "ShotUpdate.h"
#include "vectors.h"

#include <string>
#include <vector>
#include <map>
#ifdef USE_TR1
#include <tr1/memory>
#include <tr1/functional>
#define	shared_ptr	tr1::shared_ptr
#define	function	tr1::function
#else
#include <memory>
#include <functional>
#endif

/** a ShotManager is used track shots fired by players and the server
 */

namespace Shots
{
class Shot;

class FlightLogic
{
public:
	virtual ~FlightLogic() {}

	virtual void Setup(Shot& UNUSED(shot) ) {}
	virtual bool Update ( Shot& UNUSED(shot) ); // call the base class for lifetime expire
	virtual void End ( Shot& UNUSED(shot) ) {}
	virtual void Retarget ( Shot& UNUSED(shot), PlayerId UNUSED(newTarget) ) {};

	virtual bool CollideBox ( Shot& UNUSED(shot), fvec3& UNUSED(center), fvec3& UNUSED(size), float UNUSED(rotation) ) {return false;}
	virtual bool CollideSphere ( Shot& UNUSED(shot), fvec3& UNUSED(center), float UNUSED(radius) ) {return false;}
	virtual bool CollideCylinder ( Shot& UNUSED(shot), fvec3& UNUSED(center), float UNUSED(height), float UNUSED(radius) ) {return false;}

protected:
	virtual fvec3 ProjectShotLocation( Shot& shot, double deltaT );
};

typedef std::map<std::string, FlightLogic*> FlightLogicMap;

class Shot
{
protected:
	uint32_t GUID;
	FlightLogic &Logic;

	double	LifeTime;
public:
	fvec3		StartPosition;
	fvec3		LastUpdatePosition;
	double		LastUpdateTime;
	double		StartTime;

	std::map<std::string,uint32_t> MetaData;

	FiringInfo	Info;

	PlayerId	Target;

	void		*Pimple;

	Shot(uint32_t guid, const FiringInfo &info, FlightLogic& logic);
	virtual ~Shot();

	bool Update();
	void End();
	void Retarget(PlayerId target);

	uint32_t GetGUID() {return GUID;}
	uint16_t GetLocalShotID() {return Info.shot.player;}

	PlayerId GetPlayerID() {return Info.shot.player;}

	double GetLastUpdateTime() {return LastUpdateTime;}
	double GetStartTime() {return StartTime;}
	double GetLifeTime() {return LifeTime;}

	double GetLifeParam() {return (LastUpdateTime-StartTime)/LifeTime;}

	bool CollideBox ( fvec3 &center, fvec3 size, float rotation ) {return Logic.CollideBox(*this,center,size,rotation);}
	bool CollideSphere ( fvec3 &center, float radius ) {return Logic.CollideSphere(*this,center,radius);}
	bool CollideCylinder ( fvec3 &center, float height, float radius) {return Logic.CollideCylinder(*this,center,height,radius);}
};

typedef std::shared_ptr<Shot>	ShotRef;
typedef std::vector<std::shared_ptr<Shot>> ShotList;
typedef std::shared_ptr<std::function <void (Shot&)> > ShotEvent;

#ifdef USE_TR1
// limit the scope of possible side effects of these macro definitions
#undef	shared_ptr
#undef	function
#endif

#define INVALID_SHOT_GUID 0

class Manager
{
public:
  Manager();
  virtual ~Manager();

  void Init();

  void SetFlightLogic(const char* flagCode, FlightLogic* logic);

  uint32_t AddShot (const FiringInfo &info, PlayerId shooter);
  void RemoveShot (uint32_t shotID);

  void RemovePlayer (PlayerId player);

  void SetShotTarget( uint32_t shot, PlayerId target );

  uint32_t FindShotGUID (PlayerId shooter, uint16_t localShotID);
  ShotRef FindShot(uint32_t shotID) { return FindByID(shotID);}

  void Update();

  static double DeadShotCacheTime;

  ShotList	LiveShotsForPlayer(PlayerId player);
  ShotList	DeadShotsForPlayer(PlayerId player);

  ShotEvent ShotCreated;
  ShotEvent ShotEnded;

private:
	uint32_t NewGUID();
	ShotRef	FindByID(uint32_t shotID);

	double Now();

	ShotList	LiveShots;
	ShotList	RecentlyDeadShots;

	FlightLogicMap Logics;

	uint32_t	LastGUID;

};

class ProjectileShotLogic: public FlightLogic
{
public:
	virtual ~ProjectileShotLogic() {}

	virtual bool Update ( Shot& shot );
};

class GuidedMissileLogic: public ProjectileShotLogic
{
public:
	virtual ~GuidedMissileLogic() {}

	virtual void End ( Shot& UNUSED(shot) );
};

class SuperBulletLogic: public ProjectileShotLogic
{
public:
};

class ShockwaveLogic: public FlightLogic
{
public:
	virtual void Setup(Shot& shot );
	virtual bool Update ( Shot& shot );

	virtual bool CollideBox ( Shot& shot, fvec3& ecnter, fvec3& size, float rotation );
	virtual bool CollideSphere ( Shot& shot, fvec3& center, float radius );
	virtual bool CollideCylinder ( Shot& shot, fvec3& center, float height, float radius );

protected:
	bool PointInSphere ( fvec3& point, Shot& shot );
};
}
#endif  /*__SPAWNPOLICY_H__ */

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
