#include "actor.h"
#include "m_random.h"
#include "a_action.h"
#include "p_local.h"
#include "p_enemy.h"
#include "s_sound.h"
#include "a_strifeglobal.h"

static FRandom pr_entity ("Entity");

void A_SpawnEntity (AActor *);
void A_200e0 (AActor *);
void A_20c74 (AActor *);

void A_20538 (AActor *);
void A_212e4 (AActor *);
void A_2134c (AActor *);
void A_20334 (AActor *);
void A_204d0 (AActor *);
void A_SentinelBob (AActor *);
void A_TossGib (AActor *);

// Entity Nest --------------------------------------------------------------

class AEntityNest : public AActor
{
	DECLARE_ACTOR (AEntityNest, AActor)
};

FState AEntityNest::States[] =
{
	S_NORMAL (NEST, 'A', -1, NULL, NULL)
};

IMPLEMENT_ACTOR (AEntityNest, Strife, 26, 0)
	PROP_SpawnState (0)
	PROP_RadiusFixed (84)
	PROP_HeightFixed (47)
	PROP_Flags (MF_SOLID|MF_NOTDMATCH)
	PROP_StrifeType (76)
END_DEFAULTS

// Entity Pod ---------------------------------------------------------------

class AEntityPod : public AActor
{
	DECLARE_ACTOR (AEntityPod, AActor)
};

FState AEntityPod::States[] =
{
	S_NORMAL (PODD, 'A',  60, A_Look,			&States[0]),

	S_NORMAL (PODD, 'A', 360, NULL,				&States[2]),
	S_NORMAL (PODD, 'B',   9, A_NoBlocking,		&States[3]),
	S_NORMAL (PODD, 'C',   9, NULL,				&States[4]),
	S_NORMAL (PODD, 'D',   9, A_SpawnEntity,	&States[5]),
	S_NORMAL (PODD, 'E',  -1, NULL,				NULL)
};

IMPLEMENT_ACTOR (AEntityPod, Strife, 198, 0)
	PROP_SpawnState (0)
	PROP_SeeState (1)
	PROP_RadiusFixed (25)
	PROP_HeightFixed (91)
	PROP_Flags (MF_SOLID|MF_NOTDMATCH)
	PROP_StrifeType (77)
	PROP_SeeSound ("misc/gibbed")
END_DEFAULTS

// Entity Boss --------------------------------------------------------------

class AEntityBoss : public AActor
{
	DECLARE_ACTOR (AEntityBoss, AActor)
public:
	void Serialize (FArchive &arc);
	void BeginPlay ();
	void Touch (AActor *toucher);

	fixed_t SpawnX, SpawnY, SpawnZ;
};

FState AEntityBoss::States[] =
{
#define S_ENTITY_SPAWN 0
	S_NORMAL (MNAM, 'A', 100, NULL,		&States[S_ENTITY_SPAWN+1]),
	S_BRIGHT (MNAM, 'B',  60, NULL,		&States[S_ENTITY_SPAWN+2]),
	S_BRIGHT (MNAM, 'C',   4, NULL,		&States[S_ENTITY_SPAWN+3]),
	S_BRIGHT (MNAM, 'D',   4, NULL,		&States[S_ENTITY_SPAWN+4]),
	S_BRIGHT (MNAM, 'E',   4, NULL,		&States[S_ENTITY_SPAWN+5]),
	S_BRIGHT (MNAM, 'F',   4, NULL,		&States[S_ENTITY_SPAWN+6]),
	S_BRIGHT (MNAM, 'G',   4, NULL,		&States[S_ENTITY_SPAWN+7]),
	S_BRIGHT (MNAM, 'H',   4, NULL,		&States[S_ENTITY_SPAWN+8]),
	S_BRIGHT (MNAM, 'I',   4, NULL,		&States[S_ENTITY_SPAWN+9]),
	S_BRIGHT (MNAM, 'J',   4, NULL,		&States[S_ENTITY_SPAWN+10]),
	S_BRIGHT (MNAM, 'K',   4, NULL,		&States[S_ENTITY_SPAWN+11]),
	S_BRIGHT (MNAM, 'L',   4, NULL,		&States[S_ENTITY_SPAWN+12]),
	S_BRIGHT (MNAL, 'A',   4, A_Look,				&States[S_ENTITY_SPAWN+13]),
	S_BRIGHT (MNAL, 'B',   4, A_SentinelBob,		&States[S_ENTITY_SPAWN+12]),

#define S_ENTITY_SEE (S_ENTITY_SPAWN+14)
	S_BRIGHT (MNAL, 'A',   4, A_Chase,				&States[S_ENTITY_SEE+1]),
	S_BRIGHT (MNAL, 'B',   4, A_Chase,				&States[S_ENTITY_SEE+2]),
	S_BRIGHT (MNAL, 'C',   4, A_SentinelBob,		&States[S_ENTITY_SEE+3]),
	S_BRIGHT (MNAL, 'D',   4, A_Chase,				&States[S_ENTITY_SEE+4]),
	S_BRIGHT (MNAL, 'E',   4, A_Chase,				&States[S_ENTITY_SEE+5]),
	S_BRIGHT (MNAL, 'F',   4, A_Chase,				&States[S_ENTITY_SEE+6]),
	S_BRIGHT (MNAL, 'G',   4, A_SentinelBob,		&States[S_ENTITY_SEE+7]),
	S_BRIGHT (MNAL, 'H',   4, A_Chase,				&States[S_ENTITY_SEE+8]),
	S_BRIGHT (MNAL, 'I',   4, A_Chase,				&States[S_ENTITY_SEE+9]),
	S_BRIGHT (MNAL, 'J',   4, A_Chase,				&States[S_ENTITY_SEE+10]),
	S_BRIGHT (MNAL, 'K',   4, A_SentinelBob,		&States[S_ENTITY_SEE]),

#define S_ENTITY_MELEE (S_ENTITY_SEE+11)
	S_BRIGHT (MNAL, 'J',   4, A_FaceTarget,			&States[S_ENTITY_MELEE+1]),
	S_BRIGHT (MNAL, 'I',   4, A_20538,				&States[S_ENTITY_MELEE+2]),
	S_BRIGHT (MNAL, 'C',   4, NULL,					&States[S_ENTITY_SEE+2]),

#define S_ENTITY_MISSILE (S_ENTITY_MELEE+3)
	S_BRIGHT (MNAL, 'F',   4, A_FaceTarget,			&States[S_ENTITY_MISSILE+1]),
	S_BRIGHT (MNAL, 'I',   4, A_200e0,				&States[S_ENTITY_MISSILE+2]),
	S_BRIGHT (MNAL, 'E',   4, NULL,					&States[S_ENTITY_SEE+10]),

#define S_ENTITY_PAIN (S_ENTITY_MISSILE+3)
	S_BRIGHT (MNAL, 'J',   2, A_Pain,				&States[S_ENTITY_SEE+6]),

#define S_ENTITY_DIE (S_ENTITY_PAIN+1)
	S_BRIGHT (MNAL, 'L',   7, A_212e4,				&States[S_ENTITY_DIE+1]),
	S_BRIGHT (MNAL, 'M',   7, A_Scream,				&States[S_ENTITY_DIE+2]),
	S_BRIGHT (MNAL, 'N',   7, A_212e4,				&States[S_ENTITY_DIE+3]),
	S_BRIGHT (MNAL, 'O',   7, A_212e4,				&States[S_ENTITY_DIE+4]),
	S_BRIGHT (MNAL, 'P',   7, A_2134c,				&States[S_ENTITY_DIE+5]),
	S_BRIGHT (MNAL, 'Q',  64, A_212e4,				&States[S_ENTITY_DIE+6]),
	S_BRIGHT (MNAL, 'Q',   6, A_20c74,				NULL),
};

IMPLEMENT_ACTOR (AEntityBoss, Strife, 128, 0)
	PROP_StrifeType (74)
	PROP_SpawnHealth (2500)
	PROP_SpawnState (S_ENTITY_SPAWN)
	PROP_SeeState (S_ENTITY_SEE)
	PROP_PainState (S_ENTITY_PAIN)
	PROP_PainChance (255)
	PROP_MeleeState (S_ENTITY_MELEE)
	PROP_MissileState (S_ENTITY_MISSILE)
	PROP_DeathState (S_ENTITY_DIE)
	PROP_SpeedFixed (13)
	PROP_RadiusFixed (130)
	PROP_HeightFixed (200)
	PROP_Mass (1000)
	PROP_Flags (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|
				MF_FLOAT|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH|
				MF_STRIFEx8000000)
	PROP_Flags2 (MF2_PASSMOBJ|MF2_PUSHWALL|MF2_MCROSS)
	PROP_Flags3 (MF3_NOTARGET)
	PROP_Flags4 (MF4_INCOMBAT|MF4_LOOKALLAROUND|MF4_SPECTRAL|MF4_NOICEDEATH)
	PROP_MinMissileChance (150)
	PROP_RenderStyle (STYLE_Translucent)
	PROP_Alpha (TRANSLUC25)
	PROP_SeeSound ("entity/sight")
	PROP_AttackSound ("entity/melee")
	PROP_PainSound ("entity/pain")
	PROP_DeathSound ("entity/death")
	PROP_ActiveSound ("entity/active")
END_DEFAULTS

void AEntityBoss::Serialize (FArchive &arc)
{
	Super::Serialize (arc);
	arc << SpawnX << SpawnY << SpawnZ;
}

void AEntityBoss::BeginPlay ()
{
	SpawnX = x;
	SpawnY = y;
	SpawnZ = z;
}

void AEntityBoss::Touch (AActor *toucher)
{
	P_DamageMobj (toucher, this, this, 5, MOD_HIT);
}

// Second Entity Boss -------------------------------------------------------

void A_SubEntityDeath (AActor *);

class AEntitySecond : public AActor
{
	DECLARE_ACTOR (AEntitySecond, AActor)
public:
	void Touch (AActor *toucher);
};

FState AEntitySecond::States[] =
{
#define S_ENTITY2_SPAWN 0
	S_BRIGHT (MNAL, 'R', 10, A_Look,			&States[S_ENTITY2_SPAWN]),

#define S_ENTITY2_SEE (S_ENTITY2_SPAWN+1)
	S_BRIGHT (MNAL, 'R',  5, A_SentinelBob,		&States[S_ENTITY2_SEE+1]),
	S_BRIGHT (MNAL, 'S',  5, A_Chase,			&States[S_ENTITY2_SEE+2]),
	S_BRIGHT (MNAL, 'T',  5, A_Chase,			&States[S_ENTITY2_SEE+3]),
	S_BRIGHT (MNAL, 'U',  5, A_SentinelBob,		&States[S_ENTITY2_SEE+4]),
	S_BRIGHT (MNAL, 'V',  5, A_Chase,			&States[S_ENTITY2_SEE+5]),
	S_BRIGHT (MNAL, 'W',  5, A_SentinelBob,		&States[S_ENTITY2_SEE]),

#define S_ENTITY2_MELEE (S_ENTITY2_SEE+6)
	S_BRIGHT (MNAL, 'S',  4, A_FaceTarget,		&States[S_ENTITY2_MELEE+1]),
	S_BRIGHT (MNAL, 'R',  4, A_20538,			&States[S_ENTITY2_MELEE+2]),
	S_BRIGHT (MNAL, 'T',  4, A_SentinelBob,		&States[S_ENTITY2_SEE+1]),

#define S_ENTITY2_MISSILE (S_ENTITY2_MELEE+3)
	S_BRIGHT (MNAL, 'W',  4, A_FaceTarget,		&States[S_ENTITY2_MISSILE+1]),
	S_BRIGHT (MNAL, 'U',  4, A_204d0,			&States[S_ENTITY2_MISSILE+2]),
	S_BRIGHT (MNAL, 'V',  4, A_SentinelBob,		&States[S_ENTITY2_SEE+4]),

#define S_ENTITY2_PAIN (S_ENTITY2_MISSILE+3)
	S_BRIGHT (MNAL, 'R',  2, A_Pain,			&States[S_ENTITY2_SEE]),

#define S_ENTITY2_DIE (S_ENTITY2_PAIN+1)
	S_BRIGHT (MDTH, 'A',  3, A_Scream,			&States[S_ENTITY2_DIE+1]),
	S_BRIGHT (MDTH, 'B',  3, A_TossGib,			&States[S_ENTITY2_DIE+2]),
	S_BRIGHT (MDTH, 'C',  3, A_NoBlocking,		&States[S_ENTITY2_DIE+3]),
	S_BRIGHT (MDTH, 'D',  3, A_TossGib,			&States[S_ENTITY2_DIE+4]),
	S_BRIGHT (MDTH, 'E',  3, A_TossGib,			&States[S_ENTITY2_DIE+5]),
	S_BRIGHT (MDTH, 'F',  3, A_TossGib,			&States[S_ENTITY2_DIE+6]),
	S_BRIGHT (MDTH, 'G',  3, A_TossGib,			&States[S_ENTITY2_DIE+7]),
	S_BRIGHT (MDTH, 'H',  3, A_TossGib,			&States[S_ENTITY2_DIE+8]),
	S_BRIGHT (MDTH, 'I',  3, A_TossGib,			&States[S_ENTITY2_DIE+9]),
	S_BRIGHT (MDTH, 'J',  3, A_TossGib,			&States[S_ENTITY2_DIE+10]),
	S_BRIGHT (MDTH, 'K',  3, A_TossGib,			&States[S_ENTITY2_DIE+11]),
	S_BRIGHT (MDTH, 'L',  3, A_TossGib,			&States[S_ENTITY2_DIE+12]),
	S_BRIGHT (MDTH, 'M',  3, A_TossGib,			&States[S_ENTITY2_DIE+13]),
	S_BRIGHT (MDTH, 'N',  3, A_TossGib,			&States[S_ENTITY2_DIE+14]),
	S_BRIGHT (MDTH, 'O',  3, A_SubEntityDeath,	NULL)
};

IMPLEMENT_ACTOR (AEntitySecond, Strife, -1, 0)
	PROP_StrifeType (75)
	PROP_SpawnHealth (990)
	PROP_SpawnState (S_ENTITY2_SPAWN)
	PROP_SeeState (S_ENTITY2_SEE)
	PROP_PainState (S_ENTITY2_PAIN)
	PROP_PainChance (255)
	PROP_MeleeState (S_ENTITY2_MELEE)
	PROP_MissileState (S_ENTITY2_MISSILE)
	PROP_DeathState (S_ENTITY2_DIE)
	PROP_SpeedFixed (14)
	PROP_RadiusFixed (130)
	PROP_HeightFixed (200)
	PROP_Mass (1000)
	PROP_Flags (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|
				MF_FLOAT|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH|
				MF_STRIFEx8000000)
	PROP_Flags2 (MF2_PASSMOBJ|MF2_PUSHWALL|MF2_MCROSS)
	PROP_Flags4 (MF4_INCOMBAT|MF4_LOOKALLAROUND|MF4_SPECTRAL|MF4_NOICEDEATH)
	PROP_MinMissileChance (150)
	PROP_RenderStyle (STYLE_Translucent)
	PROP_Alpha (TRANSLUC25)
	PROP_SeeSound ("alienspectre/sight")
	PROP_AttackSound ("alienspectre/blade")
	PROP_PainSound ("alienspectre/pain")
	PROP_DeathSound ("alienspectre/death")
	PROP_ActiveSound ("alienspectre/active")
END_DEFAULTS

void AEntitySecond::Touch (AActor *toucher)
{
	P_DamageMobj (toucher, this, this, 5, MOD_HIT);
}

void A_SubEntityDeath (AActor *self)
{
	if (CheckBossDeath (self))
	{
		G_ExitLevel (0, false);
	}
}

void A_200e0 (AActor *self)
{
	AActor *bar;

	// Apparent Strife bug: Case 5 was unreachable because they used % 5 instead of % 6.
	// I've fixed that by making case 1 duplicate it, since case 1 did nothing.
	switch (pr_entity() % 5)
	{
	case 0:
		if (self->target != NULL)
		{
			bar = Spawn<ASpectralLightningSpot> (self->x, self->y, ONFLOORZ);
			bar->threshold = 25;
			bar->target = self;
			bar->tracer = self->target;
			bar->health = -2;
		}
		break;

	case 2:
		if (self->target != NULL)
		{
			bar = P_SpawnMissile (self, self->target, RUNTIME_CLASS(ASpectralLightningH3));
			if (bar != NULL)
			{
				bar->health = -2;
			}
		}
		break;

	case 3:
		A_20334 (self);
		break;

	case 4:
		if (self->target != NULL)
		{
			bar = P_SpawnMissile (self, self->target, RUNTIME_CLASS(ASpectralLightningBigV2));
			if (bar != NULL)
			{
				bar->health = -2;
				bar->tracer = self->target;
			}
		}
		break;

	case 1:
	case 5:
		if (self->target != NULL)
		{
			bar = P_SpawnMissile (self, self->target, RUNTIME_CLASS(ASpectralLightningBigBall2));
			if (bar != NULL)
			{
				bar->health = -2;
			}
		}
		break;
	}
}


void A_SpawnEntity (AActor *self)
{
	AActor *entity = Spawn<AEntityBoss> (self->x, self->y, self->z + 70*FRACUNIT);
	if (entity != NULL)
	{
		entity->angle = self->angle;
		entity->target = self->target;
		entity->momz = 5*FRACUNIT;
	}
}

void A_20c74 (AActor *selfa)
{
	AEntityBoss *self = static_cast<AEntityBoss *>(selfa);
	AEntitySecond *second;
	fixed_t secondRadius = GetDefault<AEntitySecond>()->radius * 2;
	angle_t an;
	
	an = self->angle >> ANGLETOFINESHIFT;
	second = Spawn<AEntitySecond> (self->SpawnX + FixedMul (secondRadius, finecosine[an]),
		self->SpawnY + FixedMul (secondRadius, finesine[an]), self->SpawnZ);
	second->target = self->target;
	A_FaceTarget (second);
	an = second->angle >> ANGLETOFINESHIFT;
	second->momx += FixedMul (finecosine[an], 320000);
	second->momy += FixedMul (finesine[an], 320000);

	an = (self->angle + ANGLE_90) >> ANGLETOFINESHIFT;
	second = Spawn<AEntitySecond> (self->SpawnX + FixedMul (secondRadius, finecosine[an]),
		self->SpawnY + FixedMul (secondRadius, finesine[an]), self->SpawnZ);
	second->target = self->target;
	second->momx = FixedMul (secondRadius, finecosine[an]) << 2;
	second->momy = FixedMul (secondRadius, finesine[an]) << 2;
	A_FaceTarget (second);

	an = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT;
	second = Spawn<AEntitySecond> (self->SpawnX + FixedMul (secondRadius, finecosine[an]),
		self->SpawnY + FixedMul (secondRadius, finesine[an]), self->SpawnZ);
	second->target = self->target;
	second->momx = FixedMul (secondRadius, finecosine[an]) << 2;
	second->momy = FixedMul (secondRadius, finesine[an]) << 2;
	A_FaceTarget (second);
}
