#pragma once

// included by game.h

BEGIN_SW_NS


class DSWActor
{
	int index;
	DSWActor* base();

public:

	int cumulDamage;

	DSWActor() :index(int(this - base())) { /*assert(index >= 0 && index < kMaxSprites);*/ }
	DSWActor& operator=(const DSWActor& other) = default;

	void Clear()
	{
	}
	bool hasU() { return User[index].Data() != nullptr; }
	/*
	void addU()
	{
		if (s().extra == -1) dbInsertXSprite(s().index);
	}
	*/
	spritetype& s() { return sprite[index]; }
	USER* u() { return User[index].Data(); }

	void SetOwner(DSWActor* own)
	{
		s().owner = own ? own->s().index : -1;
	}

	DSWActor* GetOwner()
	{
		if (s().owner == -1 || s().owner == MAXSPRITES - 1) return nullptr;
		return base() + s().owner;
	}

	int GetSpriteIndex() const
	{
		return index;
	}
};

extern DSWActor swActors[MAXSPRITES];

inline DSWActor* DSWActor::base() { return swActors; }

// Iterator wrappers that return an actor pointer, not an index.
class SWStatIterator : public StatIterator
{
public:
	SWStatIterator(int stat) : StatIterator(stat)
	{
	}

	DSWActor* Next()
	{
		int n = NextIndex();
		return n >= 0 ? &swActors[n] : nullptr;
	}

	DSWActor* Peek()
	{
		int n = PeekIndex();
		return n >= 0 ? &swActors[n] : nullptr;
	}
};

class SWSectIterator : public SectIterator
{
public:
	SWSectIterator(int stat) : SectIterator(stat)
	{
	}

	DSWActor* Next()
	{
		int n = NextIndex();
		return n >= 0 ? &swActors[n] : nullptr;
	}

	DSWActor* Peek()
	{
		int n = PeekIndex();
		return n >= 0 ? &swActors[n] : nullptr;
	}
};

// An iterator to iterate over all sprites.
class SWSpriteIterator
{
	SWStatIterator it;
	int stat = 0;

public:
	SWSpriteIterator() : it(0) {}

	DSWActor* Next()
	{
		while (stat < MAXSTATUS)
		{
			auto ac = it.Next();
			if (ac) return ac;
			stat++;
			if (stat < MAXSTATUS) it.Reset(stat);
		}
		return nullptr;
	}
};

// For iterating linearly over map spawned sprites.
class SWLinearSpriteIterator
{
	int index = 0;
public:

	void Reset()
	{
		index = 0;
	}

	DSWActor* Next()
	{
		while (index < MAXSPRITES)
		{
			auto p = &swActors[index++];
			if (p->s().statnum != MAXSTATUS) return p;
		}
		return nullptr;
	}
};


// Wrapper around the insane collision info mess from Build.
struct Collision
{
	int type;
	int index;
	int legacyVal;	// should be removed later, but needed for converting back for unadjusted code.
	DSWActor* actor;

	Collision() = default;
	Collision(int legacyval) { setFromEngine(legacyval); }

	int setNone()
	{
		type = kHitNone;
		index = -1;
		legacyVal = 0;
		actor = nullptr;
		return kHitNone;
	}

	int setSector(int num)
	{
		type = kHitSector;
		index = num;
		legacyVal = type | index;
		actor = nullptr;
		return kHitSector;
	}
	int setWall(int num)
	{
		type = kHitWall;
		index = num;
		legacyVal = type | index;
		actor = nullptr;
		return kHitWall;
	}
	int setSprite(DSWActor* num)
	{
		type = kHitSprite;
		index = -1;
		legacyVal = type | int(num - swActors);
		actor = num;
		return kHitSprite;
	}

	int setFromEngine(int value)
	{
		legacyVal = value;
		type = value & kHitTypeMask;
		if (type == 0) { index = -1; actor = nullptr; }
		else if (type != kHitSprite) { index = value & kHitIndexMask; actor = nullptr; }
		else { index = -1; actor = &swActors[value & kHitIndexMask]; }
		return type;
	}
};


inline FSerializer& Serialize(FSerializer& arc, const char* keyname, DSWActor*& w, DSWActor** def)
{
	int index = w? int(w - swActors) : -1;
	Serialize(arc, keyname, index, nullptr);
	if (arc.isReading()) w = index == -1? nullptr : &swActors[index];
	return arc;
}


END_SW_NS
