
#ifndef SENSORCELLMAP_HPP
#define SENSORCELLMAP_HPP

#pragma warning(disable : 4284)
#pragma warning(push)
#include <stlport\slist>
#include <stlport\vector>
#pragma warning(pop)


class SensorCellMap
{
  public:
	SensorCellMap()
	{
	}

	~SensorCellMap()
	{
	}

	typedef stlport::slist<MechWarrior4::MWObject*> list_type;
	typedef stlport::vector<int> checksum_type;

	class Cell
	{
	  public:
		Cell()
			: m_Checksum(0)
		{
		}

		list_type::const_iterator begin()
		{
			return (entries.begin());
		}

		list_type::const_iterator end()
		{
			return (entries.end());
		}

		void Add(MechWarrior4::MWObject* ptr)
		{
			Verify(std::find(begin(),end(),ptr) == end());

			entries.insert(entries.begin(),ptr);

			RecalcChecksum();
		}

		void Remove(MechWarrior4::MWObject* ptr)
		{
			Verify(std::find(begin(),end(),ptr) != end());

			entries.remove(ptr);

			RecalcChecksum();
		}

		void RecalcChecksum()
		{
			m_Checksum = entries.size() << 24;

			{for (list_type::const_iterator i = begin();
				  i != end();
				  ++i)
			{
				m_Checksum += ((*i)->objectID & 0x00FFFFFF);
			}}
		}

		int GetChecksum() const
		{
			return (m_Checksum);
		}

	  private:
		stlport::slist<MechWarrior4::MWObject*> entries;
		int m_Checksum;
	};

	Cell& GetCell(int x, int z)
	{
		Verify(x >= 0);
		Verify(x < WIDTH);
		Verify(z >= 0);
		Verify(z < HEIGHT);

		return (m_Entries[x][z]);
	}

	void Fill(int x1, int z1,
			  int x2, int z2,
			  list_type& output,
			  checksum_type& checksums)
	{
		{for (int i_x = x1;
			  i_x <= x2;
			  ++i_x)
		{
			{for (int i_z = z1;
				  i_z <= z2;
				  ++i_z)
			{
				Cell& cell = GetCell(i_x,i_z);

				{for (list_type::const_iterator i = cell.begin();
					  i != cell.end();
					  ++i)
				{
					output.insert(output.begin(),*i);
				}}

				checksums.push_back(cell.GetChecksum());
			}}
		}}
	}

	int CellIndex(Stuff::Scalar pos)
	{
		Verify(CELL_WIDTH == 256);
		Verify(CELL_HEIGHT == 256);

		int p((int)(pos + (Scalar)COORD_ADD));
		return (p >> 8);
	}

	void ClampIndex(int& index, int min, int max)
	{
		if (index < min)
		{
			index = min;
		}
		else
		{
			if (index > max)
			{
				index = max;
			}
		}
	}

	void GetCellPos(int& x, int& z, const Stuff::LinearMatrix4D& local_to_world)
	{
		x = CellIndex(local_to_world(W_Axis, X_Axis));
		z = CellIndex(local_to_world(W_Axis, Z_Axis));

		ClampIndex(x,0,WIDTH - 1);
		ClampIndex(z,0,HEIGHT - 1);
	}

	void GetRect(int& x1, int& z1, int& x2, int& z2, const Stuff::LinearMatrix4D& local_to_world, Stuff::Scalar radius)
	{
		Scalar x = local_to_world(W_Axis, X_Axis);
		Scalar z = local_to_world(W_Axis, Z_Axis);

		Scalar _x1 = x - radius;
		if (_x1 < (Stuff::Scalar)LEFT_BOUNDS)
		{
			_x1 = (Stuff::Scalar)LEFT_BOUNDS;
		}
		x1 = CellIndex(_x1);

		Scalar _x2 = x + radius;
		if (_x2 >= (Stuff::Scalar)RIGHT_BOUNDS)
		{
			_x2 = (Stuff::Scalar)RIGHT_BOUNDS;
		}
		x2 = CellIndex(_x2);

		Scalar _z1 = z - radius;
		if (_z1 < (Stuff::Scalar)TOP_BOUNDS)
		{
			_z1 = (Stuff::Scalar)TOP_BOUNDS;
		}
		z1 = CellIndex(_z1);

		Scalar _z2 = z + radius;
		if (_z2 >= (Stuff::Scalar)BOTTOM_BOUNDS)
		{
			_z2 = (Stuff::Scalar)BOTTOM_BOUNDS;
		}
		z2 = CellIndex(_z2);
	}

	void Fill(const Stuff::LinearMatrix4D& local_to_world,
			  Stuff::Scalar radius,
			  list_type& output,
			  checksum_type& checksums)
	{
		checksums.clear();

		int x1, z1, x2, z2;
		GetRect(x1,z1,x2,z2,local_to_world,radius);
		Fill(x1,z1,x2,z2,output,checksums);
	}

	bool ChecksumMatches(const Stuff::LinearMatrix4D& local_to_world,
						 Stuff::Scalar radius,
						 checksum_type& checksums)
	{
		int x1, z1, x2, z2;
		GetRect(x1,z1,x2,z2,local_to_world,radius);

		if (checksums.size() != (x2 - x1 + 1) * (z2 - z1 + 1))
		{
			return (false);
		}

		checksum_type::const_iterator i_checksum = checksums.begin();

		{for (int i_x = x1;
			  i_x <= x2;
			  ++i_x)
		{
			{for (int i_z = z1;
				  i_z <= z2;
				  ++i_z)
			{
				// TODO: REWRITE THIS BEFORE CHECKING IN!!!
				Cell& cell = GetCell(i_x,i_z);
				if (cell.GetChecksum() != *(i_checksum))
				{
					return (false);
				}

				++i_checksum;
			}}
		}}

		return (true);
	}

	void Add(MechWarrior4::MWObject* ptr, int x, int z)
	{
		Cell& cell = GetCell(x,z);

		cell.Add(ptr);
	}

	void Add(MechWarrior4::MWObject* ptr, const Stuff::LinearMatrix4D& local_to_world)
	{
		int x, z;
		GetCellPos(x,z,local_to_world);
		Add(ptr,x,z);
	}

	void Remove(MechWarrior4::MWObject* ptr, int x, int z)
	{
		Cell& cell = GetCell(x,z);

		cell.Remove(ptr);
	}

	void Remove(MechWarrior4::MWObject* ptr, const Stuff::LinearMatrix4D& local_to_world)
	{
		int x, z;
		GetCellPos(x,z,local_to_world);
		Remove(ptr,x,z);
	}

  private:
	enum { CELL_WIDTH = 256 };
	enum { CELL_HEIGHT = CELL_WIDTH };
	enum { WIDTH = 20 };
	enum { HEIGHT = 20 };
	enum { COORD_ADD = 2560 };
	enum { GRID_WIDTH = WIDTH * CELL_WIDTH };
	enum { GRID_HEIGHT = HEIGHT * CELL_HEIGHT };
	enum { LEFT_BOUNDS = -COORD_ADD + 1 };
	enum { TOP_BOUNDS = -COORD_ADD + 1 };
	enum { RIGHT_BOUNDS = COORD_ADD - 1 };
	enum { BOTTOM_BOUNDS = COORD_ADD - 1 };

	Cell m_Entries[WIDTH][HEIGHT];
};


#endif // SENSORCELLMAP_HPP