#pragma once

template<typename Pixel, typename PixelData>
class raw_base_iterator
{
public:
	raw_base_iterator(PixelData* pixels, int pitch, int width, int height) :
	  m_pixels(pixels), m_line(pixels), m_pitch(pitch), m_width(width), m_height(height), m_x(0), m_y(0)
	{}
	  
	virtual ~raw_base_iterator()
	{}

	bool move(int x, int y)
	{
		if (!is_inrect(x, y))
			return false;

		m_x = x;
		m_y = y;
		m_line = m_pixels + m_pitch * m_y;
		return true;
	}
	bool next()
	{
		if (m_x >= m_width)
			return false;

		m_x++;
		return true;
	}
	int find(Pixel test)
	{
		while (m_x != m_width)
		{
			if (m_line[m_x] == test)
				return m_x;
			m_x++;
		}
		return -1;
	}
	int skip(Pixel test)
	{
		while (m_x != m_width)
		{
			if (m_line[m_x] != test)
				return m_x;
			m_x++;
		}
		return -1;
	}
	bool next_line()
	{
		if (!ret_line())
			return false;

		new_line();
		return true;
	}
	void new_line()
	{
		m_x = 0;
	}
	bool ret_line()
	{
		m_line += m_pitch;
		m_y++;

		if (m_y >= m_height)
			return false;

		return true;
	}


	bool getx(Pixel& dst)
	{
		if (!is_inwidth(m_x))
			return false;

		dst = m_line[m_x];
		return true;
	}
	bool gets(int len, Pixel* dst_pixels)
	{
		int end = m_x + len;
		if (!is_inwidth(end))
			return false;

		quick_gets(len, dst_pixels);
		return true;
	}
	void quick_gets(int len, Pixel* dst_pixels)
	{
		memcpy(dst_pixels, m_line + m_x, len * sizeof(Pixel));
		m_x += len;
	}

	const Pixel* assert_refs(int len)
	{
		assert(can_refs(len));
		return quick_refs(len);
	}
	const Pixel* quick_refs(int len)
	{
		const Pixel* ret = quick_peeks(len);
		m_x += len;
		return ret;
	}
	const Pixel* assert_peeks(int len)
	{
		assert(can_refs(len));
		return quick_peeks(len);
	}
	const Pixel* quick_peeks(int len)
	{
		return m_line + m_x;
	}

	bool can_refs(int len)
	{
		return is_inwidth(m_x + len - 1);
	}
	int get_width()
	{
		return m_width;
	}
	int get_height()
	{
		return m_height;
	}
	bool is_inrect(int x, int y)
	{
		return (is_inwidth(x) && is_inheight(y));
	}
	bool is_inwidth(int x)
	{
		return (x >= 0 && x < m_width);
	}
	bool is_inheight(int y)
	{
		return (y >= 0 && y < m_height);
	}
	
protected:
	PixelData* m_line;
	PixelData* m_pixels;
	int m_pitch;
	int m_width;
	int m_height;
	
	int m_x;
	int m_y;
};

template<typename Pixel>
class raw_const_iterator : public raw_base_iterator<Pixel, const Pixel>
{
public:
	raw_const_iterator(const Pixel* pixels, int pitch, int width, int height) :
	  raw_base_iterator<Pixel, const Pixel>(pixels, pitch, width, height)
	{}

	virtual ~raw_const_iterator()
	{}
};


template<typename Pixel>
class raw_iterator : public raw_base_iterator<Pixel, Pixel>
{
public:
	raw_iterator(Pixel* pixels, int pitch, int width, int height) : raw_base_iterator<Pixel, Pixel>(pixels, pitch, width, height)
	{}
	  
	virtual ~raw_iterator()
	{}

	
	bool fill(int len, Pixel src)
	{
		int end = m_x + len;
		if (!is_inwidth(end))
			return false;

		while (m_x != end)
			m_line[m_x++] = src;
		return true;
	}
	void fill_line(Pixel src)
	{
		for (int i = 0; i != m_width; ++i)
			m_line[i] = src;
	}
	bool putx(Pixel src)
	{
		if (!is_inwidth(m_x))
			return false;

		m_line[m_x] = src;
		return true;
	}
	bool puts(int len, const Pixel* src_pixels)
	{
		int end = m_x + len;
		if (!is_inwidth(end))
			return false;

		quick_puts(len, src_pixels);
		return true;
	}
	void quick_puts(int len, const Pixel* src_pixels)
	{
		memcpy(m_line + m_x, src_pixels, len * sizeof(Pixel));
		m_x += len;
	}

};

template<typename Pixel>
class raw 
{
public:
	typedef raw_iterator<Pixel> iterator;

public:
	raw(int width, int height)
	{
		m_width = width;
		m_height = height;
		m_pixels = new Pixel[m_width*m_height];
	}
	virtual ~raw()
	{
		delete [] m_pixels;
	}
	void clear(Pixel src)
	{
		int size = m_width * m_height;

		for (int i = 0; i != size; ++i)
			m_pixels[i] = src;
	}
	bool fill(int x, int y, int len, Pixel src)
	{
		if (!can_refs(x, y, len))
			return false;

		quick_fill(x, y, len, src);
		return true;
	}
	bool putx(int x, int y, Pixel src)
	{
		if (!is_inrect(x, y))
			return false;

		Pixel& ref = quick_refx(x, y);
		ref = src;
		return true;
	}
	bool getx(int x, int y, Pixel& dst)
	{
		if (!is_inrect(x, y))
			return false;

		dst = quick_refx(x, y);
		return true;
	}
	bool puts(int x, int y, int len, const Pixel* src_pixels)
	{
		if (!can_refs(x, y, len))
			return false;

		Pixel* dst_pixels = quick_refs(x, y, len);	
		memcpy(dst_pixels, src_pixels, len * sizeof(Pixel));
		return true;
	}
	bool gets(int x, int y, int len, Pixel* dst_pixels)
	{
		if (!can_refs(x, y, len))
			return false;

		Pixel* src_pixels = &quick_refx(x, y);	
		memcpy(dst_pixels, src_pixels, len * sizeof(Pixel));
		return true;
	}
	Pixel* assert_refs(int x, int y, int len)
	{
		assert(can_refs(x, y, len));
		return quick_refs(x, y, len);
	}
	Pixel* quick_refs(int x, int y, int len)
	{
		return m_pixels + y * m_width + x;
	}

	void assert_fill(int x, int y, int len, Pixel src)
	{
		assert(can_refs(x, y, len));
		quick_fill(x, y, len, src);
	}
	void quick_fill(int x, int y, int len, Pixel src)
	{
		Pixel* cur = quick_refs(x, y, len);	
		Pixel* end = cur + len;
		while (cur != end)
			*cur++ = src;
	}
	Pixel& assert_refx(int x, int y)
	{
		assert(is_inrect(x, y));
		return quick_refx(x, y);
	}
	Pixel& quick_refx(int x, int y)
	{
		return m_pixels[y * m_width + x];
	}
	bool is_inrect(int x, int y)
	{
		return (is_inwidth(x) && is_inheight(y));
	}
	bool is_inwidth(int x)
	{
		return (x >= 0 && x < m_width);
	}
	bool is_inheight(int y)
	{
		return (y >= 0 && y < m_height);
	}
	bool can_refs(int x, int y, int len)
	{
		return is_inwidth(x) && is_inheight(y) && is_inwidth(x + len - 1);
	}
	
	iterator get_iterator()
	{
		return iterator(m_pixels, m_width, m_width, m_height);
	}

protected:
	Pixel*		m_pixels;
	int	m_width;
	int	m_height;
};
