//Registers and cleans up all casings
class GibQueue : Thinker
{
	int queuePos; //increments and loops around every element of the array
	int queueSize; //the size, set at the beginning of each level
	Array<Actor> gibs; //The magic
	
	//Constructs the handler. Runs once per level.
	GibQueue Init()
	{
		ChangeStatNum(STAT_INFO);
		queuePos = 0;
		queueSize = CVar.FindCVar("egr_clean_gore_queue_size").GetInt();
		return self;
	}
	
	//Creates or gets the queue, used to access the object
	static GibQueue Get()
	{
		ThinkerIterator it = ThinkerIterator.Create("GibQueue",STAT_INFO);
		let instance = GibQueue(it.Next());
		if (instance == null)
		{
			instance = new("GibQueue").Init();
		}
		return instance;
	}
	
	/*
		Here's the real magic bit. The array starts out empty, so what happens whenever this is called is that the object that is passed (the casing)
	is inserted into the array at the end, up until the point where the array is "full".
		Once it's "full", the position number is used to insert elements. When the function is called, if there is a valid pointer to the object that is
	already being managed (the old casing), that object is destroyed and the object which was passed in is inserted in its place. The position number 
	is then incremented in a way which wraps around once it reaches the end of the array.
		What this means is that the oldest existing casings get destroyed to make space for new ones on demand, with no need for timers or other hackery.
	I don't know if this kind of handler has been used in other mods but feel free to copy it.
	*/
	void Manage(Actor gib)
	{
		if(CVar.FindCVar("egr_clean_gore").GetInt() == 0)
			return;
		if(gibs.Size() < queueSize)
		{
			gibs.Push(gib);
		}
		else
		{
			let oldGib = Actor(gibs[queuePos]);
			if(oldGib)
			{
				oldGib.Destroy();
			}
			gibs[queuePos] = gib;
			queuePos = (queuePos+1)%queueSize;
		}
	}
}

/*
	A ton of stuff below is taken from Smooth Doom (and modified heavily) or adapted from the 64kb weapon mod project thing
	If, by some chance, the smooth doom guys see this feel free to take anything you want of course. My implementation was 
	more efficient than the pure decorate version it was based on, so if you're still using that try these.
*/

class BabelBlood : Blood Replaces Blood
{
	default
	{
		Radius 1;
		Height 1;
		Mass 1;
		Gravity 0.125;
		+NOBLOCKMAP;
		+NOTELEPORT;
		+FORCEXYBILLBOARD;
	}
	States
	{
	Spawn:
		BLUD C 5;
		TNT1 AAA 0 A_SpawnItemEx("BabelBloodSpurt", 0,0,0, frandom(-2.0, 2.0),frandom(-2.0, 2.0),frandom(1.5,4.5), 0, SXF_TRANSFERTRANSLATION | SXF_CLIENTSIDE,128);
		BLUD B 4 A_SetGravity(1);
		BLUD A 3;
		Stop;
	Spray:
		SPRY ABCDEF 3;
		SPRY G 2;
		Stop;
	}
}

class BabelBloodSpurt : Actor
{
	default
	{
		Radius  1;
		Height  1;
		Gravity 1;
		Scale   0.5;
		Mass    1;
		+DOOMBOUNCE;
		+FLOORCLIP;
		+DONTSPLASH;
		+NOTELEPORT;
		+NOBLOCKMAP;
		+THRUACTORS;
		+FORCEXYBILLBOARD;
	}
	States
	{
	Spawn:
		BLUD CCCCBBBBAAAA 1 NoDelay A_SpawnItemEx("BabelBloodTrail",frandom(-0.5, 0.5),frandom(-0.5, 0.5),frandom(-0.5, 0.5),0,0,0,0, SXF_TRANSFERTRANSLATION | SXF_CLIENTSIDE,0);
		Stop;
	}
}

class BabelBloodTrail : BabelBloodSpurt
{
	default
	{
		Gravity 1;
	}
	States
	{
	Spawn:
		BLUD C 0 NoDelay;
		BLUD CBA 2;
		Stop;
	}
}

//Fuck you graf, why can't I just extend this into actor?
class SplashableActor : Actor
{
	action state A_Splash(bool big = false)
	{
		string suffix = "small";
		if(big)
		{
			suffix = "big";
		}
		if(CVar.FindCVar("egr_particle_toggle").GetInt() == 1)
		{
			if(BabelLib.FloorIsLiquid(invoker))
			{
				int floorType = BabelLib.LiquidFloorType(invoker);
				int density = CVar.FindCvar("egr_particle_density").GetInt(); //avoid recomputation
				if(floorType != -1)
				{
					int mul = big ? 8 : 5;
					double size = big ? frandom(3.0, 7.0) : frandom(2.0, 5.0);
					for(int i = 0; i < (density+1)*mul; i++)
					{
						invoker.A_SpawnParticle (BabelLib.GetFloorTypeColor(floorType), 0, random(10, 20), size, frandom(0,360), 
								cos(random(0,360)),sin(random(0,360)),0, 
								frandom(-1.0, 1.0),frandom(-1.0, 1.0),frandom(2.0,5.0), 
								0,0,-0.4, 0.98, -1);
					}
					invoker.A_PlaySound(BabelLib.GetFloorTypeSound(floorType) .. suffix);
					return ResolveState("Disappear");
				}
				else
				{
					//Hit an unidentified floor somehow
					Console.Printf("ERROR: Somehow found a floor that is a liquid, but isnt?");
					return ResolveState(null);
				}
			}
		}
		return ResolveState(null);
	}
}

class Bits : SplashableActor
{
	default
	{
		Radius 3;
		Height 3;
		Speed 7;
		Scale 1.0;
		Mass 1;
		+NOBLOCKMAP;
		+MISSILE;
		+NOTELEPORT;
		+MOVEWITHSECTOR;
		+CLIENTSIDEONLY;
		+BLOODLESSIMPACT;
		+DONTSPLASH;
		+FLOORCLIP;
		decal "Bloodsmear";
		Gravity 0.7;
	}
	override void BeginPlay()
	{
		super.BeginPlay();
		GibQueue.Get().Manage(self);
	}
    States
    {
		Spawn:
			GIBS C 3 NoDelay;
			Loop;
		Death:
			TNT1 A 0 A_Splash();
			TNT1 A 0 A_Jump(256, random(1, 13));
			GIBS BDEFGHIJKLMNO 1 A_Jump(256, "Finish");
		Finish:
			GIBS "#" 3 A_Splash();
		Done:
			GIBS "#" 1;
			loop;
		Disappear:
			"####" "#" 1;
			stop;
    }
}

class Chunks: Bits //big gibs
{
    States
    {
		Spawn:
			GIBS C 3 NoDelay;
			Loop;
		Death:
			TNT1 A 0 A_Splash(true);
			POL5 A 3;
		Done:
			POL5 A 1;
			Loop;
		Disappear:
			"####" "#" 1;
			stop;
    }
}

class SlowChunks : Chunks
{
	default
	{
		Speed 3;
	}
}

class FlyingBlood : Actor
{
	default
	{
		Radius 3;
		Height 3;
		Speed 4;
		Scale 1.0;
		Mass 1;
		+NOBLOCKMAP;
		+MISSILE;
		+NOTELEPORT;
		+MOVEWITHSECTOR;
		+CLIENTSIDEONLY;
		+BLOODLESSIMPACT;
		-DONTSPLASH;
		+FLOORCLIP;
		-DOOMBOUNCE;
		-SOLID;
		+THRUACTORS;
		decal "bloodsplat";
		Gravity 0.7;
	}
    States
    {
		Spawn:
			TNT1 A 0 NoDelay A_Jump(256, random(1,2));
			BLUD FG 1 A_Jump(256, "Fly");
		Fly:
			BLUD "#" 2;
			Loop;
		Death:
			BLUD F 1;
			Stop;
		Crash:
			BLUD F 1;
			TNT1 A 0 A_SpawnItem("BloodSpot", 0, 5);
			Stop;
    }
}

class GreenFlyingBlood : FlyingBlood
{
	default
	{
		translation "0:255=%[0.1,0.1,0.1]:[0.0,2.0,0.0]";
		decal "greenbloodsplat";
	}
	States
    {
    Spawn:
		TNT1 A 0 NoDelay A_Jump(256, random(1,2));
		BLUD FG 1 A_Jump(256, "Fly");
	Fly:
		BLUD "#" 2;
		Loop;
	Death:
		BLUD F 1;
		TNT1 A 0 A_SpawnItem("GreenBloodSpot", 0, 5);
		Stop;
    }
}

class BlueFlyingBlood : FlyingBlood
{
	default
	{
		translation "32:47=240:243", "176:191=199:207"; //Translation taken from Smooth Doom, as the desaturated version looks like shit. Thanks SD dudes
		decal "bluebloodsplat";
	}
	States
    {
    Spawn:
		TNT1 A 0 NoDelay A_Jump(256, random(1,2));
		BLUD FG 1 A_Jump(256, "Fly");
	Fly:
		BLUD "#" 2;
		Loop;
	Death:
		BLUD F 1;
		TNT1 A 0 A_SpawnItem("BlueBloodSpot", 0, 5);
		Stop;
    }
}

class FlyingBloodFast: FlyingBlood
{
	default
	{
		Speed 6;
		+FORCEXYBILLBOARD;
		Scale 0.5;
		Gravity 1.0;
	}
}

class GreenFlyingBloodFast : GreenFlyingBlood
{
	default
	{
		Speed 6;
		+FORCEXYBILLBOARD;
		Scale 0.5;
		Gravity 1.0;
		translation "0:255=%[0.1,0.1,0.1]:[0.0,2.0,0.0]";
		decal "greenbloodsplat";
	}
}

class BlueFlyingBloodFast : BlueFlyingBlood
{
	default
	{
		Speed 6;
		+FORCEXYBILLBOARD;
		Scale 0.5;
		Gravity 1.0;
		translation "32:47=240:243", "176:191=199:207"; //Translation taken from Smooth Doom, as the desaturated version looks like shit. Thanks SD dudes
		decal "bluebloodsplat";
	}
}

class FlyingBloodFaster: FlyingBloodFast
{
	default
	{
		Scale 0.3;
		Speed 12;
	}
}

class GreenFlyingBloodFaster : GreenFlyingBloodFast
{
	default
	{
		Scale 0.3;
		Speed 12;
		translation "0:255=%[0.1,0.1,0.1]:[0.0,2.0,0.0]";
		decal "GreenBloodSmear";
	}
}

class BlueFlyingBloodFaster : BlueFlyingBloodFast
{
	default
	{
		Scale 0.3;
		Speed 12;
		translation "32:47=240:243", "176:191=199:207"; //Translation taken from Smooth Doom, as the desaturated version looks like shit. Thanks SD dudes
		decal "BlueBloodSmear";
	}
}

class BloodSpew : Actor
{
	default
	{
		+FORCEXYBILLBOARD;
		+GHOST;
		+NOBLOCKMAP;
		+NOGRAVITY;
		+NOCLIP;
		-DONTSPLASH;
		+ALLOWPARTICLES;
		decal "bloodsplat";
		Speed 0;
	}

	states
	{
		Spawn:
			TNT1 A 0 NoDelay;
			TNT1 AA 0 
			{
				A_SpawnProjectile("FlyingBlood", 0, 0, random (0, 360), CMF_OFFSETPITCH, -random (0, 160));
				A_SpawnProjectile("FlyingBloodFaster", 0, 0, random (0, 360), CMF_OFFSETPITCH, -random (0, 180));
			}
			stop;
		Disappear:
			TNT1 A 1;
			stop;
	}
}

class GreenBloodSpew : BloodSpew
{
	default
	{
		translation "0:255=%[0.1,0.1,0.1]:[0.0,2.0,0.0]";
		decal "greenbloodsplat";
	}
	states
	{
		Spawn:
			TNT1 A 0 NoDelay;
			TNT1 AA 0 
			{
				A_SpawnProjectile("GreenFlyingBlood", 0, 0, random (0, 360), CMF_OFFSETPITCH, -random (0, 160));
				A_SpawnProjectile("GreenFlyingBloodFast", 0, 0, random (0, 360), CMF_OFFSETPITCH, -random (0, 180));
			}
			stop;
		Disappear:
			TNT1 A 1;
			stop;
	}
}

class BlueBloodSpew : BloodSpew
{
	default
	{
		translation "32:47=240:243", "176:191=199:207"; //Translation taken from Smooth Doom, as the desaturated version looks like shit. Thanks SD dudes
		decal "bluebloodsplat";
	}
	states
	{
		Spawn:
			TNT1 A 0 NoDelay;
			TNT1 AA 0 
			{
				A_SpawnProjectile("BlueFlyingBlood", 0, 0, random (0, 360), CMF_OFFSETPITCH, -random (0, 160));
				A_SpawnProjectile("BlueFlyingBloodFast", 0, 0, random (0, 360), CMF_OFFSETPITCH, -random (0, 180));
			}
			stop;
		Disappear:
			TNT1 A 1;
			stop;
	}
}

class BloodSpot : SplashableActor
{
	default
	{
		RenderStyle "Translucent";
		Alpha 1;
		radius 12;
		height 2;
		mass 1;
		scale 0.8;
		+NOTELEPORT;
		-NOBLOCKMAP;
		+FORCEXYBILLBOARD;
		+DOOMBOUNCE;
		+DONTSPLASH;
		+MISSILE;
		Speed 0;
		BounceFactor 0.01;
	}
	override void BeginPlay()
	{
		super.BeginPlay();
		GibQueue.Get().Manage(self);
	}
	states
	{
		Spawn:
			BLUD F 1 NoDelay;
			Loop;
		Death:
			TNT1 A 0 A_Splash();//A_JumpIf(BabelLib.FloorIsLiquid(self),"Disappear");
			TNT1 A 0 A_Jump(256, Random(1,4));
			BLD1 ABCD 1 A_Jump(256, "Done");
		Done:
			BLD1 "#" 1;
			Loop;
		Disappear:
			"####" "#" 1;
			stop;
	}
}

class GreenBloodSpot : BloodSpot
{
	default
	{
		translation "0:255=%[0.1,0.1,0.1]:[0.0,2.0,0.0]";
		decal "greenbloodsplat";
	}
	states
	{
		Spawn:
			BLUD F 1 NoDelay;
			Loop;
		Death:
			TNT1 A 0 A_JumpIf(BabelLib.FloorIsLiquid(self),"Disappear");
			TNT1 A 0 A_Jump(256, Random(1,4));
			BLD1 ABCD 1 A_Jump(256, "Done");
		Done:
			BLD1 "#" 1;
			Loop;
		Disappear:
			"####" "#" 1;
			stop;
	}
}

class BlueBloodSpot : BloodSpot
{
	default
	{
		translation "32:47=240:243", "176:191=199:207"; //Translation taken from Smooth Doom, as the desaturated version looks like shit. Thanks SD dudes
		decal "bluebloodsplat";
	}
	states
	{
		Spawn:
			BLUD F 1 NoDelay;
			Loop;
		Death:
			TNT1 A 0 A_JumpIf(BabelLib.FloorIsLiquid(self),"Disappear");
			TNT1 A 0 A_Jump(256, Random(1,4));
			BLD1 ABCD 1 A_Jump(256, "Done");
		Done:
			BLD1 "#" 1;
			Loop;
		Disappear:
			"####" "#" 1;
			stop;
	}
}