/*
 * Licensed under BSD license.  See LICENCE.TXT  
 *
 * Produced by:	Jeff Lait
 *
 *      	7DRL Development
 *
 * NAME:        grid.h ( Mage Library, C++ )
 *
 * COMMENTS:
 *	Implements a grid of POD
 *	TFW you find out you aleady had texture.h to deal with 2d maps!
 *	I knew I had something and looked for a long time before rewriting.
 *	This is *NOT* shared COW, however, so is a bit different...
 *	So I shall tell myself as I cry myself to sleep.
 */

#ifndef __grid_h__
#define __grid_h__

#include <iterator>
#include "mygba.h"
#include "ptrlist.h"
#include "vec2.h"

template <typename T>
class GRID_T
{
public:
    GRID_T() { myWidth = myHeight = 0; ::memset(&myDefVal, 0, sizeof(T)); }
    GRID_T(int w, int h) { setSize(w, h); ::memset(&myDefVal, 0, sizeof(T)); }
    ~GRID_T() {}

    void	setSize(int w, int h)
    { 
	myData.resize(w * h); 
	myWidth = w; myHeight = h;
    }

    void	constant(T val)
    { myData.constant(val); }

    void	setDefVal(T val) { myDefVal = val; }
    T		defval() const { return myDefVal; }

    void		clear(int x, int y, int w, int h, T val)
    {
	for (int i = 0; i < h; i++)
	    for (int j = 0; j < w; j++)
		set(x+j, y+i, val);
    }

    void		replace(T oldval, T newval)
    {
	int		x, y;
	FORALL_XY(x, y)
	{
	    if (get(x, y) == oldval)
		set(x, y, newval);
	}
    }

    bool	isValid(int x, int y) const
    {
	if (x < 0 || x >= width()) return false;
	if (y < 0 || y >= height()) return false;
	return true;
    }

    void		wrap(int &x, int &y) const
    { x = x % width();  if (x < 0) x += width();
      y = y % height(); if (y < 0) y += height(); }
    void		wrap(IVEC2 &p) const
    { wrap(p.x(), p.y()); }

    /// Operator is raw access so is not bounds checked.
    const T	&operator()(IVEC2 p) const
    { return (*this)(p.x(), p.y()); }
    const T	&operator()(int x, int y) const
    { 
	J_ASSERT(isValid(x, y));
	return myData(x + y * width()); 
    }
    T	&operator()(IVEC2 p)
    { return (*this)(p.x(), p.y()); }
    T		&operator()(int x, int y)
    { 
	J_ASSERT(isValid(x, y));
	return myData(x + y * width()); 
    }

    /// get/set are safe so return background or ignore.
    T		 get(int x, int y) const 
    { 
	if (isValid(x, y)) return (*this)(x, y);
	return myDefVal;
    }
    T		get(IVEC2 p) const
    { return get(p.x(), p.y()); }

    T		wrapget(int x, int y) const
    { wrap(x, y); return get(x, y); }
    T		wrapget(IVEC2 p) const
    { wrap(p); return get(p); }

    void	set(int x, int y, T val)
    {
	if (isValid(x, y)) (*this)(x, y) = val;
    }
    void	set(IVEC2 p, T val) const
    { return set(p.x(), p.y(), val); }

    int		width() const { return myWidth; }
    int		height() const { return myHeight; }

protected:
    int		myWidth, myHeight;
    PTRLIST<T>  myData;
    T		myDefVal;
};

using GRID8 = GRID_T<u8>;
using GRIDI = GRID_T<int>;
using GRIDB = GRID_T<bool>;

#endif
