// $Id: CoordIndex.h 8019 2007-07-14 22:30:11Z trick $

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Deadly Rooms of Death.
 *
 * The Initial Developer of the Original Code is
 * Caravel Software.
 * Portions created by the Initial Developer are Copyright (C) 1995, 1996, 
 * 1997, 2000, 2001, 2002 Caravel Software. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef COORDINDEX_H
#define COORDINDEX_H

//CoordIndex.h
//Declarations for CCoordIndex.
//Class for indexing X,Y coordinates.  Should be used for quick membership
//checks.  Be careful about using to track coords over a large area because
//the index can take a lot of memory.  For a 38 x 32 room, 1216+ bytes 
//will be used.
//
//Additionally, special values can be stored at X,Y coordinates.

#include "Types.h"
#include "AttachableObject.h"
#include "Assert.h"

template <typename T>
class CCoordIndex_T : public CAttachableObject
{
public:
	CCoordIndex_T(const UINT wCols=0, const UINT wRows=0);
	CCoordIndex_T(const CCoordIndex_T<T>& coordIndex);
	virtual ~CCoordIndex_T();

	void        Add(const UINT wX, const UINT wY, const T val=1);
	void        Clear();
	bool        Exists(const UINT wX, const UINT wY) const;
	const T *   GetIndex() const {return this->pbytIndex;}
	DWORD       GetSize() const {return this->dwCoordCount;}
	T           GetAt(const UINT wX, const UINT wY) const;
	T&          GetAt(const UINT wX, const UINT wY);
	bool        Init(const UINT wSetCols, const UINT wSetRows);
	void        Remove(const UINT wX, const UINT wY);

private:
	CCoordIndex_T &operator= (const CCoordIndex_T<T>& Src); //not implemented

	DWORD dwCoordCount;
	UINT  wCols, wRows;
	T *   pbytIndex;
};

typedef CCoordIndex_T<BYTE> CCoordIndex;


//
//CCoordIndex public template methods.
//

//*******************************************************************************
template<typename T>
CCoordIndex_T<T>::CCoordIndex_T(
//Constructor.
//
//Params:
	const UINT wCols, const UINT wRows) //[default = (0,0)]
	: CAttachableObject()
	, dwCoordCount(0L)
	, wCols(0), wRows(0)
	, pbytIndex(NULL)
{
	if (wCols && wRows)
		Init(wCols, wRows);
}

//*******************************************************************************
template<typename T>
CCoordIndex_T<T>::CCoordIndex_T(const CCoordIndex_T<T>& coordIndex)
	: CAttachableObject()
	, dwCoordCount(0L)
	, wCols(0), wRows(0)
	, pbytIndex(NULL)
{
	if (coordIndex.wCols && coordIndex.wRows)
	{
		if (Init(coordIndex.wCols, coordIndex.wRows))
			//works only for primitive types
			memcpy(this->pbytIndex, coordIndex.pbytIndex,
					coordIndex.wCols * coordIndex.wRows * sizeof(T));
	}
}

//*******************************************************************************
template<typename T>
CCoordIndex_T<T>::~CCoordIndex_T()
//Destructor.
{
	delete[] this->pbytIndex;
}

//*******************************************************************************
template<typename T>
bool CCoordIndex_T<T>::Init(
//Must be called before other methods to alloc the index.
//
//Params:
	const UINT wSetCols, //(in) Defines size of internal index and range of
	const UINT wSetRows) //(in) coords that other methods may accept.
//
//Returns:
//True if index was allocated, false if not.
{
	ASSERT(wSetCols > 0);
	ASSERT(wSetRows > 0);

	//500 = unreasonably large and probably an error.
	ASSERT(wSetCols < 500);
	ASSERT(wSetRows < 500);

	//Check whether object is already inited to these dimensions.
	if (this->wCols == wSetCols && this->wRows == wSetRows)
	{
		Clear();
		return true;
	}

	//Delete existing index storage.
	if (this->pbytIndex)
	{
		delete[] this->pbytIndex;
		this->pbytIndex = NULL;
		this->dwCoordCount = 0L;
	}

	//Create new index storage.
	this->pbytIndex = new T[wSetCols * wSetRows];
	if (this->pbytIndex)
	{
		this->wCols = wSetCols;
		this->wRows = wSetRows;
		Clear();
		return true;
	}

	//To make assertions fire in other methods that have been incorrectly 
	//called.
	this->wCols = this->wRows = 0;
	return false;
}

//*******************************************************************************
template<typename T>
void CCoordIndex_T<T>::Add(
//Adds a set of coords to the index.
//
//Params:
	const UINT wX, const UINT wY,
	const T val)               //(in) default=1
{
	ASSERT(wX < this->wCols && wY < this->wRows);
	ASSERT(val != 0); //zero is considered the reset value
	const DWORD dwSquareI = wY * this->wCols + wX;
	ASSERT(this->pbytIndex);
	if (this->pbytIndex[dwSquareI] == 0)
		++this->dwCoordCount;
	ASSERT(this->dwCoordCount <= this->wCols * this->wRows);
	this->pbytIndex[dwSquareI] = val;
}

//*******************************************************************************
template<typename T>
void CCoordIndex_T<T>::Remove(
//Removes a set of coords to the index.
//
//Params:
	const UINT wX, const UINT wY)
{
	ASSERT(wX < this->wCols && wY < this->wRows);
	const DWORD dwSquareI = wY * this->wCols + wX;
	ASSERT(this->pbytIndex);
	if (this->pbytIndex[dwSquareI])
	{
		--this->dwCoordCount;
		ASSERT(this->dwCoordCount <= this->wCols * this->wRows);
		this->pbytIndex[dwSquareI] = 0;
	}
}

//*******************************************************************************
template<typename T>
void CCoordIndex_T<T>::Clear()
//Removes all coords from the index.
{
	if (this->pbytIndex)
		memset(this->pbytIndex, 0, this->wCols * this->wRows * sizeof(T));
	this->dwCoordCount = 0L;
}

//*******************************************************************************
template<typename T>
bool CCoordIndex_T<T>::Exists(
//Are coords currently in the index?
//
//Params:
	const UINT wX, const UINT wY)
//
//Returns:
//True if they are, false if not.
const
{
	return GetAt(wX,wY) != 0;
}

//*******************************************************************************
template<typename T>
T CCoordIndex_T<T>::GetAt(
//
//
//Params:
	const UINT wX, const UINT wY)
//
//Returns:
//Value stored at (wX,wY)
const
{
	ASSERT(wX < this->wCols && wY < this->wRows);
	ASSERT(this->pbytIndex);
	return this->pbytIndex[wY * this->wCols + wX];
}

//*******************************************************************************
template<typename T>
T& CCoordIndex_T<T>::GetAt(
//
//
//Params:
	const UINT wX, const UINT wY)
//
//Returns:
//Value stored at (wX,wY)
{
	ASSERT(wX < this->wCols && wY < this->wRows);
	ASSERT(this->pbytIndex);
	return this->pbytIndex[wY * this->wCols + wX];
}


#endif //...#ifndef COORDINDEX_H
