/* 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	__PLAYER_H__
#define	__PLAYER_H__

#include "common.h"

/* system headers */
#include <string>

/* common interface headers */
#include "global.h"
#ifndef BUILDING_BZADMIN
#include "bzfgl.h"
#endif
#include "TimeKeeper.h"
#include "Address.h"
#include "Flag.h"
#include "PlayerState.h"
#include "ShotStatistics.h"


/* local interface headers */
class ShotPath;
class SceneDatabase;
class TankSceneNode;
class TankIDLSceneNode;
class SphereSceneNode;
class Obstacle;
class TankDeathOverride;

// 54 bytes
const int PlayerUpdatePLenMax =
  sizeof(float)		+ // timestamp
  PlayerIdPLen		+ // player id
  sizeof(int32_t)	+ // order
  sizeof(int16_t)	+ // status
  sizeof(float) * 3	+ // position			(or int16_t * 3)
  sizeof(float) * 3	+ // velocity			(or int16_t * 3)
  sizeof(float)		+ // angle			(or int16_t)
  sizeof(float)		+ // angular velocity		(or int16_t)
  sizeof(int16_t)	+ // jump jets			(conditional)
  sizeof(int32_t)	+ // physics driver		(conditional)
  sizeof(int16_t)	+ // user speed			(conditional)
  sizeof(int16_t)	+ // user angular velocity	(conditional)
  sizeof(uint8_t);	  // sounds			(conditional)


class Player {
public:
  Player(const PlayerId&, TeamColor,
	 const char* callsign, const char* motto,
	 const PlayerType);
  virtual	~Player();

  PlayerId	getId() const;
  TeamColor	getTeam() const;
  void		setTeam(TeamColor);
  void		updateTank(float dt, bool local);
  const char*	getCallSign() const;
  const char*	getMotto() const;
  void		setMotto(const char* _motto);
  PlayerType	getPlayerType() const;
  FlagType*	getFlag() const;
  long		getOrder() const;
  short		getStatus() const;
  const float*	getPosition() const;
  float		getAngle() const;
  const float*	getForward() const;
  const float*	getVelocity() const;

  float		getAngularVelocity() const;
  int		getPhysicsDriver() const;
  int		getDeathPhysicsDriver() const;
  float		getRadius() const;
  void		getMuzzle(float*) const;
  float		getMuzzleHeight() const;
  short		getWins() const;
  short		getLosses() const;
  short		getTeamKills() const;
  float		getTKRatio() const;
  float		getNormalizedScore() const;
  float		getLocalNormalizedScore() const;
  short		getScore() const;
  const float*	getDimensions() const;

  const float*	getApparentVelocity() const;

#ifndef BUILDING_BZADMIN
  inline const float*	getColor() const
  {
	  return color;
  }
#endif
  short		getRabbitScore() const;
  short		getSelfKills() const;
  short		getLocalWins() const;
  short		getLocalLosses() const;
  short		getLocalTeamKills() const;
  const TimeKeeper&	getExplodeTime() const;
  const TimeKeeper&	getTeleportTime() const;
  short		getFromTeleporter() const;
  short		getToTeleporter() const;
  float		getTeleporterProximity() const;
  virtual int		getMaxShots() const;
  virtual ShotPath*	getShot(int index) const = 0;

  const ShotStatistics*	getShotStatistics() const;

  void		addToScene(SceneDatabase*, TeamColor effectiveTeam,
			   bool inCockpit, bool seerView,
			   bool showTreads, bool showIDL);

  bool		getIpAddress(Address&);
  void		setIpAddress(const Address& addr);

  virtual void	addShots(SceneDatabase*, bool colorblind) const;
  void		setLandingSpeed(float velocity);
  void		spawnEffect();
  void		fireJumpJets();

  bool		isAlive() const;
  bool		isPaused() const;
  bool		isFalling() const;
  bool		isFlagActive() const;
  bool		isTeleporting() const;
  bool		isExploding() const;
  bool		isPhantomZoned() const;
  bool		isCrossingWall() const;
  bool		isNotResponding() const;
  void		resetNotResponding();
  bool		isHunted() const;
  void		setHunted(bool _hunted);
  bool		isAutoPilot() const;
  void		setAutoPilot(bool = true);
  bool		isAdmin() const;
  void		setAdmin(bool = true);
  bool		isRegistered() const;
  void		setRegistered(bool = true);
  bool		isVerified() const;
  void		setVerified(bool = true);
  bool		hasPlayerList() const;
  void		setPlayerList(bool = true);

  bool		getPausedMessageState ( void ) const;
  void		setPausedMessageState ( bool set ) { pauseMessageState = set;}

  bool		validTeamTarget(const Player *possibleTarget) const;

  // returns true iff dead reckoning is too different from the
  // current tank state.
  bool		isDeadReckoningWrong() const;

  // update state based on dead reckoning
  void		doDeadReckoning();

  // called to update state according to incoming packets
  void		move(const float* pos, float azimuth);
  void		setVelocity(const float* velocity);
  void		setAngularVelocity(float);
  void		setPhysicsDriver(int);
  void		setRelativeMotion();
  void		setUserSpeed(float speed);
  void		setUserAngVel(float angvel);
  void		changeTeam(TeamColor);
  virtual void	setFlag(FlagType*);
  virtual void	changeScore(short deltaWins, short deltaLosses, short deltaTeamKills);
  void		changeSelfKills(short delta);
  void		changeLocalScore(short deltaWins, short deltaLosses, short deltaTeamKills);
  void		setHandicap(float handicap);
  void		setStatus(short);
  void		setExplode(const TimeKeeper&);
  void		setTeleport(const TimeKeeper&, short from, short to);
  void		endShot(int index, bool isHit = false,
			bool showExplosion = false);
  void		addHitToStats(FlagType* flag);

  void*		pack(void*, uint16_t& code);
  const void*	unpack(const void*, uint16_t code);

  void		setDeadReckoning();
  void		setDeadReckoning(float timestamp);

  void		setUserTexture ( const char *tex ) { if (tex) userTexture = tex;}

  void		renderRadar() const;

  void		setExplodePos( const float * p);

  void setZpos (float z);

  float getMaxSpeed ( void ) const;

  void forceReload(float time);

  void setDeathEffect ( TankDeathOverride *e );
  TankDeathOverride* getDeathEffect ( void );

  int reportedHits;
  int computedHits;
  std::map<int,bool>	hitMap;
protected:
  void	  clearRemoteSounds();
  void	  addRemoteSound(int sound);

protected:
  // shot statistics
  ShotStatistics	shotStatistics;
  const Obstacle* lastObstacle; // last obstacle touched
  TimeKeeper	jamTime;

  // pause message
  bool pauseMessageState;

  std::vector<ShotPath*> shots;
  float		  handicap;

private:
  // return true if the shot had to be terminated or false if it
  // was already terminated.  position must be set to the shot's
  // position if you return true (it's okay to return false if
  // there's no meaningful shot position).
  virtual bool	doEndShot(int index, bool isHit, float* position) = 0;
  void getDeadReckoning(float* predictedPos, float* predictedAzimuth,
			float* predictedVel, float time) const;
  void calcRelativeMotion(float vel[2], float& speed, float& angvel);
  void setVisualTeam (TeamColor team );
  void updateFlagEffect(FlagType* flag);
  void updateTranslucency(float dt);
  void updateDimensions(float dt, bool local);
  void updateTreads(float dt);
  void updateJumpJets(float dt);
  void updateTrackMarks();
  bool hitObstacleResizing();

private:
  // data not communicated with other players
  bool			notResponding;
  bool			hunted;

  // credentials
  PlayerId		id;
  bool			admin;
  bool			registered;
  bool			verified;
  bool			playerList;
  Address		ipAddr;
  bool			haveIpAddr;

  // data use for drawing
  TankSceneNode*	tankNode;
  TankIDLSceneNode*	tankIDLNode;
  SphereSceneNode*	pausedSphere;
#ifndef BUILDING_BZADMIN
  GLfloat		color[4];
  GLfloat		teleAlpha;
#endif
  std::string		userTexture;
  static int		tankTexture;
  static int		tankOverideTexture;
  TeamColor		lastVisualTeam;
  TimeKeeper		lastTrackDraw;

  // permanent data
  TeamColor		team;			// my team

  char			callSign[CallSignLen];	// my pseudonym
  char			motto[MottoLen];	// my motto
  PlayerType		type;			// Human/Computer

  // relatively stable data
  FlagType*		flagType;		// flag type I'm holding
  float			dimensions[3];		// current tank dimensions
  float			dimensionsScale[3];	// use to scale the dimensions
  float			dimensionsRate[3];	 // relative to scale
  float			dimensionsTarget[3];	// relative to scale
  bool			useDimensions;		// use the varying dimensions for gfx
  float			alpha;			// current tank translucency
  float			alphaRate;		// current tank translucency
  float			alphaTarget;		// current tank translucency
  TimeKeeper		spawnTime;		// time I started spawning
  TimeKeeper		explodeTime;		// time I started exploding
  TimeKeeper		teleportTime;		// time I started teleporting
  short			fromTeleporter;		// teleporter I entered
  short			toTeleporter;		// teleporter I exited
  float			teleporterProximity;	// how close to a teleporter
  short			wins;			// number of kills
  short			losses;			// number of deaths
  short			tks;			// number of teamkills
  short			selfKills;			// number of self-destructions

  // score of local player against this player
  short			localWins;		// local player won this many
  short			localLosses;		// local player lost this many
  short			localTks;		// local player team killed this many

  // highly dynamic data
  PlayerState		state;

  // additional state
  bool			autoPilot;

  // computable highly dynamic data
  float			forward[3];		// forward unit vector

  // relative motion information
  float			relativeSpeed;		// relative speed
  float			relativeAngVel;		// relative angular velocity

  float			apparentVelocity[3];	// velocity of tank as derived from it's last positional update

  // dead reckoning stuff
  TimeKeeper inputTime;		// time of input
  float inputTimestamp;	 // input timestamp of sender
  int	inputStatus;		// tank status
  float	inputPos[3];		// tank position
  float	inputVel[3];		// tank velocity
  float	inputAzimuth;		// direction tank is pointing
  float	inputAngVel;		// tank turn rate
  bool	inputTurning;		// tank is turning
  float inputRelVel[2];		// relative velocity
  float	inputRelSpeed;		// relative speed
  float	inputRelAngVel;		// relative angular velocity
  float	inputTurnCenter[2];	// tank turn center
  float	inputTurnVector[2];	// tank turn vector
  int	inputPhyDrv;		// physics driver

  // average difference between time source and time destination
  float			deltaTime;

  // time offset on last measurement
  float			offset;

  // 0 -> not received any sample
  // 1 -> 1 sample rx
  // 2 -> 2 or more sample rx
  int			deadReckoningState;

  int			oldStatus;		// old tank status bits
  float			oldZSpeed;		// old tank vertical speed
};

// shot data goes in LocalPlayer or RemotePlayer so shot type isn't lost.

//
// Player
//

inline PlayerId		Player::getId() const
{
  return id;
}

inline TeamColor	Player::getTeam() const
{
  return team;
}

inline void		Player::setTeam(TeamColor _team)
{
  team = _team;
}

inline const char*	Player::getCallSign() const
{
  return callSign;
}

inline const char*	Player::getMotto() const
{
  return motto;
}

inline PlayerType	Player::getPlayerType() const
{
  return type;
}

inline FlagType*	Player::getFlag() const
{
  return flagType;
}

inline long		Player::getOrder() const
{
  return state.order;
}

inline short		Player::getStatus() const
{
  return state.status;
}

inline const float*	Player::getPosition() const
{
  return state.pos;
}

inline float		Player::getAngle() const
{
  return state.azimuth;
}

inline const float*	Player::getDimensions() const
{
  return dimensions;
}

inline const float*	Player::getForward() const
{
  return forward;
}

inline const float*	Player::getVelocity() const
{
  return state.velocity;
}

inline const float*	Player::getApparentVelocity() const
{
  return apparentVelocity;
}

inline float		Player::getAngularVelocity() const
{
  return state.angVel;
}

inline int		Player::getPhysicsDriver() const
{
  return state.phydrv;
}

inline short		Player::getWins() const
{
  return wins;
}

inline short		Player::getLosses() const
{
  return losses;
}

inline short		Player::getTeamKills() const
{
  return tks;
}

inline short		Player::getSelfKills() const
{
  return selfKills;
}

inline short		Player::getLocalWins() const
{
  return localWins;
}

inline short		Player::getLocalLosses() const
{
  return localLosses;
}

inline short		Player::getLocalTeamKills() const
{
  return localTks;
}

inline short		Player::getScore() const
{
  return wins - losses;
}

inline const TimeKeeper	&Player::getExplodeTime() const
{
  return explodeTime;
}

inline const TimeKeeper	&Player::getTeleportTime() const
{
  return teleportTime;
}

inline short		Player::getFromTeleporter() const
{
  return fromTeleporter;
}

inline short		Player::getToTeleporter() const
{
  return toTeleporter;
}

inline float		Player::getTeleporterProximity() const
{
  return teleporterProximity;
}

inline const ShotStatistics*  Player::getShotStatistics() const
{
  return &shotStatistics;
}

inline bool		Player::isAlive() const
{
  return (state.status & short(PlayerState::Alive)) != 0;
}

inline bool		Player::isPaused() const
{
  return (state.status & short(PlayerState::Paused)) != 0;
}

inline bool Player::getPausedMessageState ( void ) const
{
	return pauseMessageState;
}

inline bool		Player::isAutoPilot() const
{
  return autoPilot;
}

inline void		Player::setAutoPilot(bool autopilot)
{
  autoPilot = autopilot;
}

inline bool		Player::isFalling() const
{
  return (state.status & short(PlayerState::Falling)) != 0;
}

inline bool		Player::isFlagActive() const
{
  return (state.status & short(PlayerState::FlagActive)) != 0;
}

inline bool		Player::isTeleporting() const
{
  return (state.status & short(PlayerState::Teleporting)) != 0;
}

inline bool		Player::isExploding() const
{
  return (state.status & short(PlayerState::Exploding)) != 0;
}

inline bool		Player::isPhantomZoned() const
{
  return (isFlagActive() && (getFlag() == Flags::PhantomZone));
}

inline bool		Player::isCrossingWall() const
{
  return (state.status & short(PlayerState::CrossingWall)) != 0;
}

inline bool		Player::isNotResponding() const
{
  return notResponding;
}

inline void		Player::resetNotResponding()
{
  notResponding = false;
}

inline bool		Player::isHunted() const
{
  return hunted;
}

inline void		Player::setHunted(bool _hunted)
{
  hunted = _hunted;
}

inline bool		Player::isAdmin() const
{
  return admin;
}

inline void		Player::setAdmin(bool _admin)
{
  admin = _admin;
}

inline bool		Player::isRegistered() const
{
  return registered;
}

inline void		Player::setRegistered(bool _registered)
{
  registered = _registered;
}

inline bool		Player::isVerified() const
{
  return verified;
}

inline void		Player::setVerified(bool _verified)
{
  verified = _verified;
}

inline bool		Player::hasPlayerList() const
{
  return playerList;
}

inline void		Player::setPlayerList(bool _playerList)
{
  playerList = _playerList;
}

inline void		Player::addHitToStats(FlagType* flag)
{
  shotStatistics.recordHit(flag);
}

inline void*		Player::pack(void* buf, uint16_t& code)
{
  setDeadReckoning();
  return state.pack(buf, code);
}

inline void Player::setZpos (float z)
{
  state.pos[2] = z;
}

#endif /* __PLAYER_H__ */

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