/*
Solar Conquest
Copyright (C) 2006 Greg Beaman

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define MAP_SIZE 1000
#define TILE_SIZE 10

//Node for object list
class CObjectListNode
{
public:
	void* obj;
	CObjectListNode* prevNode;
	CObjectListNode* nextNode;
	
	CObjectListNode(void* store);
	~CObjectListNode();
};

CObjectListNode::CObjectListNode(void* store) { obj = store; prevNode = NULL; nextNode = NULL; }
CObjectListNode::~CObjectListNode() {}

//Class to hold a list of objects
class CObjectList
{
public:
	CObjectListNode* firstNode;
	
	CObjectList();
	~CObjectList();
	
	CObjectListNode* AddNode(void* obj);
	CObjectListNode* GetNode(void* obj);
	void DeleteNode(CObjectListNode* node);
	void ClearAll();
};

CObjectList::CObjectList() { firstNode = NULL; }
CObjectList::~CObjectList() { ClearAll(); }

CObjectListNode* CObjectList::AddNode(void* obj)
{
	if (obj == NULL)
		return NULL;

	CObjectListNode* newNode;
	newNode = new CObjectListNode(obj);
	if (!newNode)
		return NULL;
	newNode->nextNode = firstNode;
	if (firstNode)
		firstNode->prevNode = newNode;
	firstNode = newNode;
	return newNode;
}

CObjectListNode* CObjectList::GetNode(void* obj)
{
	CObjectListNode* cNode = firstNode;
	while (cNode)
	{
		if (cNode->obj == obj)
			return cNode;
		cNode = cNode->nextNode;
	}
	return NULL;
}

void CObjectList::DeleteNode(CObjectListNode* node)
{
	if (!node)
		return;

	if (node->prevNode)
		node->prevNode->nextNode = node->nextNode;
	else
		firstNode = node->nextNode;
	if (node->nextNode)
		node->nextNode->prevNode = node->prevNode;
	delete node;
}

void CObjectList::ClearAll()
{
	CObjectListNode* cNode = firstNode;
	CObjectListNode* tNode;
	while (cNode)
	{
		tNode = cNode->nextNode;
		delete cNode;
		cNode = tNode;
	}
	firstNode = NULL;
}

//Data for tile
struct TileData
{
	CObjectList* list;
};

class CMap
{
protected:
	TileData Tiles[MAP_SIZE][MAP_SIZE];

public:
	CMap();
	~CMap();
	
	bool ValidTile(int x, int y);
	
	void ResetMap();

	void AddObject(float x, float y, int radius, void* obj);
	void DeleteObject(float x, float y, int radius, void* obj);
	
	CObjectList* GetObjectList(int x, int y);
	CObjectList* GetObjectList(float x, float y);
};

CMap::CMap()
{
	for (int x=0; x < MAP_SIZE; x++)
		for (int y=0; y < MAP_SIZE; y++)
		{
			Tiles[x][y].list = new CObjectList();
			if (!Tiles[x][y].list)
				DebugMessage(1,"Could not create object list for tile");
		}
}

CMap::~CMap()
{
	for (int x=0; x < MAP_SIZE; x++)
		for (int y=0; y < MAP_SIZE; y++)
		{
			delete Tiles[x][y].list;
			Tiles[x][y].list = NULL;
		}
}

void CMap::ResetMap()
{
	for (int x=0; x < MAP_SIZE; x++)
		for (int y=0; y < MAP_SIZE; y++)
			if (Tiles[x][y].list)
				Tiles[x][y].list->ClearAll();
}

bool CMap::ValidTile(int x, int y)
{
	if (x < 0 || x >= MAP_SIZE)
		return false;
	if (y < 0 || y >= MAP_SIZE)
		return false;
	return true;
}

void CMap::AddObject(float x, float y, int radius, void* obj)
{
	int sx,sy;
	sx = (int)(x / TILE_SIZE);
	sy = (int)(y / TILE_SIZE);
	
	for (int tx = sx-radius; tx <= sx+radius; tx++)
	{
		for (int ty = sy-radius; ty <= sy+radius; ty++)
		{
			if (ValidTile(tx,ty))
				if (Tiles[tx][ty].list)
					Tiles[tx][ty].list->AddNode(obj);
		}
	}
}

void CMap::DeleteObject(float x, float y, int radius, void* obj)
{
	int sx,sy;
	sx = (int)(x / TILE_SIZE);
	sy = (int)(y / TILE_SIZE);
	
	for (int tx = sx-radius; tx <= sx+radius; tx++)
	{
		for (int ty = sy-radius; ty <= sy+radius; ty++)
		{
			if (ValidTile(tx,ty))
				if (Tiles[tx][ty].list)
					Tiles[tx][ty].list->DeleteNode(Tiles[tx][ty].list->GetNode(obj));
		}
	}
}

CObjectList* CMap::GetObjectList(int x, int y)
{
	if (ValidTile(x,y))
		return Tiles[x][y].list;
	return NULL;
}

CObjectList* CMap::GetObjectList(float x, float y)
{
	int sx,sy;
	sx = (int)(x / TILE_SIZE);
	sy = (int)(y / TILE_SIZE);
	
	if (ValidTile(sx,sy))
		return Tiles[sx][sy].list;
	return NULL;
}

CMap* g_Map = NULL;
