#ifndef _H_ENTITY
#define _H_ENTITY

#include <SDL_rwops.h>
#include <stdbool.h>

#include "Audio.h"
#include "Bitmap.h"
#include "Item.h"
#include "rovdefs.h"
#include "RPK.h"

/* Hardcoded player state indices, these are appended to the end of the table */
#define PLAYER_STATE 0
#define PLAYER_STATE_HEAL 1
#define PLAYER_STATE_HURT 2
#define PLAYER_STATE_DIE 3

/* In-game time, measured in ticks. These should only be modified from functions called by Game_Tick */
typedef int time;

struct ObjState;
struct ObjType;

enum StatModifierTarget
{
	STATMOD_STRENGTH,
	STATMOD_SPEED,
	STATMOD_REGEN,
};

struct StatModifier
{
	enum StatModifierTarget target;
	float modifier;
	time duration;
};

#define MAX_STATMODS 4

#define ENTITY_TEAM_ENEMIES 0
#define ENTITY_TEAM_PLAYERS 1
#define ENTITY_TEAM_NEUTRAL 2 // Won't be targeted by anything
#define ENTITY_TEAM_BOSS 255

typedef struct _Object
{
	unsigned short id;
	short data; /* Custom data field; usage depends on the object type */
	short health, maxHealth;
	float x, y, z;
	float xd, yd, zd;
	float accel; /* Applied to velocity every game tick */
	float rot;
	time stateTimer;
	time invulnerableTime;
	time waterLerp; /* Used to transition between default z and in-water z */
	short stateId;
	short typeId;
	unsigned char isSwimming;
	unsigned char unused;
	unsigned char defense;
	unsigned char team;
	struct StatModifier modifiers[MAX_STATMODS];
	const struct _Object *dmgsrc;
	const struct _Object *owner;
	const struct ObjState *state;
	const struct ObjType *type;
} Obj_t;

typedef void(*ObjStateFunc_t)(Obj_t *);

struct ObjType
{
	int maxHealth;
	int team;
	int xpReward;
	struct
	{
		int base;
		int wallhit;
		int hurt;
		int die;
		int use;
	} states;
	int touchBytecodeOffs;    /* Offset into the bytecode block where this entity's "on touch" action is defined, or 0 for no action */
	int hitBytecodeOffs;
	float hitboxRadius;
	char spawnTile;
	short initialData;
	int particleType;
};

struct ObjState
{
	int sprite;
	int bytecodeOffs;          /* Offset into the bytecode block where this state's action is defined, or 0 for no action */
	int soundId;
	int flags;
	int next;                  /* Index of the next state, or -1 if the sprite should remain in this state. */
	int delay;                 /* Number of game ticks until the state is updated. */
};

extern int SpecialEntities[NUM_SPECIAL_DEFS];

void AI_Init(const RPKFile *);
void AI_Cleanup(void);

bool AI_IsOpposingTeam(int teamA, int teamB); /* Returns true if teamA should attack teamB */
bool AI_CanSeeAnEnemy(Obj_t *obj);
bool AI_CanSeeAnAlly(Obj_t *obj);
void AI_CalcEnemyDirVector(Obj_t *obj, float *xd, float *yd);
void AI_CalcAllyDirVector(Obj_t *obj, float *xd, float *yd);
void AI_OffsetDirVector(float *xd, float *yd, float offs);
void AI_SetMovingDir(Obj_t *obj, float speed, float accel, float xa, float ya);
int AI_HitscanAttack(Obj_t *obj, int damage, int maxHits, float dir, float maxRange, bool playSound);
int AI_SweepHit(Obj_t *obj, float radius, int damage, bool playSound);
int AI_RadiusHit(Obj_t *obj, float radius, int damage, bool playSound);
void AI_FireProjectile(Obj_t *obj, int entityId, float speed, int initialData, float angle);
void AI_SummonBurst(Obj_t *obj, int entityId, int count, float radius, int initialData);

int Obj_EntityIdByName(const char *name);
char Obj_SpawnTileForType(int type);

Obj_t Obj_Init(int type, float x, float y);

void Obj_Tick(Obj_t *, void *);
void Obj_Heal(Obj_t *, int amt);
void Obj_Knockback(Obj_t *obj, float fromx, float fromy, int damage);
int Obj_Hurt(Obj_t *, const Obj_t *src, int dmg);
void Obj_SetState(Obj_t *, int);

void Obj_AddStatModifier(Obj_t *, enum StatModifierTarget, float value, time duration);
float Obj_GetModifierValue(Obj_t *, enum StatModifierTarget);

#endif
