#include "tetris.h"
#include "renderer.h"

//enum CFigure::E_MASK
//{
//	M_QUAD,
//	M_PALKA_HORIZ,
//	M_PALKA_VERT,
//	M_G_0,
//	M_G_90,
//	M_G_180,
//	M_G_270,
//	M_G_INV_0,
//	M_G_INV_90,
//	M_G_INV_180,
//	M_G_INV_270,
//	M_S_HORIZ,
//	M_S_VERT,
//	M_S_INV_HORIZ,
//	M_S_INV_VERT,
//	M_TRI_0,
//	M_TRI_90,
//	M_TRI_180,
//	M_TRI_270
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_QUAD>
//{
//	static const unsigned short mask = 0XCC00;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_PALKA_HORIZ>
//{
//	static const unsigned short mask = 0X0F00;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_PALKA_VERT>
//{
//	static const unsigned short mask = 0X4444;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_0>
//{
//	static const unsigned short mask = 0X0644;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_90>
//{
//	static const unsigned short mask = 0X0470;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_180>
//{
//	static const unsigned short mask = 0X2260;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_270>
//{
//	static const unsigned short mask = 0X0E20;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_INV_0>
//{
//	static const unsigned short mask = 0X6220;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_INV_90>
//{
//	static const unsigned short mask = 0X0E80;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_INV_180>
//{
//	static const unsigned short mask = 0X4460;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_G_INV_270>
//{
//	static const unsigned short mask = 0X02E0;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_S_HORIZ>
//{
//	static const unsigned short mask = 0X06C0;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_S_VERT>
//{
//	static const unsigned short mask = 0X4620;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_S_INV_HORIZ>
//{
//	static const unsigned short mask = 0X0C60;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_S_INV_VERT>
//{
//	static const unsigned short mask = 0X2640;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_TRI_0>
//{
//	static const unsigned short mask = 0X04E0;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_TRI_90>
//{
//	static const unsigned short mask = 0X2620;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_TRI_180>
//{
//	static const unsigned short mask = 0X0E40;
//};
//
//template<>
//struct CFigure::TMask<CFigure::M_TRI_270>
//{
//	static const unsigned short mask = 0X4640;
//};
//
//const unsigned short CFigure::_masks[] =
//{
//	TMask<M_QUAD>::mask,
//	TMask<M_PALKA_HORIZ>::mask,
//	TMask<M_PALKA_VERT>::mask,
//	TMask<M_G_0>::mask,
//	TMask<M_G_90>::mask,
//	TMask<M_G_180>::mask,
//	TMask<M_G_270>::mask,
//	TMask<M_G_INV_0>::mask,
//	TMask<M_G_INV_90>::mask,
//	TMask<M_G_INV_180>::mask,
//	TMask<M_G_INV_270>::mask,
//	TMask<M_S_HORIZ>::mask,
//	TMask<M_S_VERT>::mask,
//	TMask<M_S_INV_HORIZ>::mask,
//	TMask<M_S_INV_VERT>::mask,
//	TMask<M_TRI_0>::mask,
//	TMask<M_TRI_90>::mask,
//	TMask<M_TRI_180>::mask,
//	TMask<M_TRI_270>::mask
//};
//
//CFigure::CFigure(E_TYPE type, E_ROTATE rotate, const TField &field):
//	_field(field), _column(3), _row(0)
//{
//	switch (type)
//	{
//	case T_QUAD:
//		_maskIdx = M_QUAD;
//		break;
//	case T_PALKA:
//		switch (rotate)
//		{
//		case R_0:
//		case R_180:
//			_maskIdx = M_PALKA_HORIZ;
//			break;
//		case R_90:
//		case R_270:
//			_maskIdx = M_PALKA_VERT;
//			break;
//		}
//		break;
//	case T_G:
//		switch (rotate)
//		{
//		case R_0:
//			_maskIdx = M_G_0;
//			break;
//		case R_90:
//			_maskIdx = M_G_90;
//			break;
//		case R_180:
//			_maskIdx = M_G_180;
//			break;
//		case R_270:
//			_maskIdx = M_G_270;
//			break;
//		}
//		break;
//	case T_G_INV:
//		switch (rotate)
//		{
//		case R_0:
//			_maskIdx = M_G_INV_0;
//			break;
//		case R_90:
//			_maskIdx = M_G_INV_90;
//			break;
//		case R_180:
//			_maskIdx = M_G_INV_180;
//			break;
//		case R_270:
//			_maskIdx = M_G_INV_270;
//			break;
//		}
//		break;
//	case T_S:
//		switch (rotate)
//		{
//		case R_0:
//		case R_180:
//			_maskIdx = M_S_HORIZ;
//			break;
//		case R_90:
//		case R_270:
//			_maskIdx = M_S_VERT;
//			break;
//		}
//		break;
//	case T_S_INV:
//		switch (rotate)
//		{
//		case R_0:
//		case R_180:
//			_maskIdx = M_S_INV_HORIZ;
//			break;
//		case R_90:
//		case R_270:
//			_maskIdx = M_S_INV_VERT;
//			break;
//		}
//		break;
//	case T_TRI:
//		switch (rotate)
//		{
//		case R_0:
//			_maskIdx = M_TRI_0;
//			break;
//		case R_90:
//			_maskIdx = M_TRI_90;
//			break;
//		case R_180:
//			_maskIdx = M_TRI_180;
//			break;
//		case R_270:
//			_maskIdx = M_TRI_270;
//			break;
//		}
//		break;
//	}
//}
//
//void CFigure::MoveRight()
//{
//	++_column;
//	if (Collide())
//		--_column;
//}
//
//void CFigure::MoveLeft()
//{
//	--_column;
//	if (Collide())
//		++_column;
//}
//
//bool CFigure::MoveDown()
//{
//	++_row;
//	if (Collide())
//	{
//		--_row;
//		return false;
//	}
//	return true;
//}
//
//void CFigure::Rotate(bool cw)
//{
//	E_MASK new_mask_idx;
//	switch (_maskIdx)
//	{
//	case M_QUAD: return;
//	case M_PALKA_HORIZ:
//		new_mask_idx = M_PALKA_VERT;
//		break;
//	case M_PALKA_VERT:
//		new_mask_idx = M_PALKA_HORIZ;
//		break;
//	case M_G_0:
//		new_mask_idx = cw ? M_G_270 : M_G_90;
//		break;
//	case M_G_90:
//		new_mask_idx = cw ? M_G_0 : M_G_180;
//		break;
//	case M_G_180:
//		new_mask_idx = cw ? M_G_90 : M_G_270;
//		break;
//	case M_G_270:
//		new_mask_idx = cw ? M_G_180 : M_G_0;
//		break;
//	case M_G_INV_0:
//		new_mask_idx = cw ? M_G_INV_270 : M_G_INV_90;
//		break;
//	case M_G_INV_90:
//		new_mask_idx = cw ? M_G_INV_0 : M_G_INV_180;
//		break;
//	case M_G_INV_180:
//		new_mask_idx = cw ? M_G_INV_90 : M_G_INV_270;
//		break;
//	case M_G_INV_270:
//		new_mask_idx = cw ? M_G_INV_180 : M_G_INV_0;
//		break;
//	case M_S_HORIZ:
//		new_mask_idx = M_S_VERT;
//		break;
//	case M_S_VERT:
//		new_mask_idx = M_S_HORIZ;
//		break;
//	case M_S_INV_HORIZ:
//		new_mask_idx = M_S_INV_VERT;
//		break;
//	case M_S_INV_VERT:
//		new_mask_idx = M_S_INV_HORIZ;
//		break;
//	case M_TRI_0:
//		new_mask_idx = cw ? M_TRI_270 : M_TRI_90;
//		break;
//	case M_TRI_90:
//		new_mask_idx = cw ? M_TRI_0 : M_TRI_180;
//		break;
//	case M_TRI_180:
//		new_mask_idx = cw ? M_TRI_90 : M_TRI_270;
//		break;
//	case M_TRI_270:
//		new_mask_idx = cw ? M_TRI_180 : M_TRI_0;
//		break;
//	}
//	if (!Collide(new_mask_idx))
//		_maskIdx = new_mask_idx;
//}
//
//bool CFigure::Collide() const
//{
//	return Collide(_maskIdx);
//}
//
//// result should be corrected by column intersection matrix
//unsigned short CFigure::GetFieldTileMask(int tileX, int tileY) const
//{
//	if (tileX >= 0 && tileX < 3 && tileY >=0 && tileY < 5) return _field[tileY][tileX];
//	if (tileX < 0 || tileX >= 3 || tileY >= 5) return ~0;
//	if (tileX < 2) return 0;
//	return 0XCCCC;
//}
//
//bool CFigure::Collide(E_MASK mask) const
//{
//	// load tiles from field wich have non-empty intersection with figure tile (like bilinear texture filtering)
//	// then extract intersection region from each loaded tile
//
//	// need round to -inf => use '>>' instead of '/' and '&' instead of '%'
//	int tile_x = _column >> 2, tile_y = _row >> 2;
//	unsigned int offset_x = _column & 3, offset_y = _row & 3;
//
//	// calculate intersection mask (it may be worth using precalculated masks)
//	unsigned short column_intersection_mask = ~((unsigned short)~0u >> offset_x);
//	column_intersection_mask |= column_intersection_mask >> 4;
//	column_intersection_mask |= column_intersection_mask >> 8;
//
//	// right up
//	unsigned short field_mask = GetFieldTileMask(tile_x, tile_y + 1) << 4 - offset_x >> offset_y * 4 & column_intersection_mask;
//
//	// right down
//	field_mask |= GetFieldTileMask(tile_x + 1, tile_y + 1) << 4 - offset_x << (4 - offset_y) * 4 & column_intersection_mask;
//
//	column_intersection_mask = ~column_intersection_mask;
//
//	// left up
//	field_mask |= GetFieldTileMask(tile_x, tile_y) >> offset_x >> offset_y * 4 & column_intersection_mask;
//
//	// left down
//	field_mask |= GetFieldTileMask(tile_x + 1, tile_y) >> offset_x << (4 - offset_y) * 4 & column_intersection_mask;
//
//	// consider early out
//	return field_mask & _masks[_maskIdx];
//}
//
//template<class F>
//void CFigure::Traverse(F callback) const
//{
//	for (unsigned int row = 0; row < 4; row++)
//		for (unsigned int column = 0; column < 4; column++)
//			if ((*this)[row][column])
//				callback(row, column);
//}

enum CFigure::E_OFFSETS
{
	O_QUAD,
	O_PALKA_HORIZ,
	O_PALKA_VERT,
	O_G_0,
	O_G_90,
	O_G_180,
	O_G_270,
	O_G_INV_0,
	O_G_INV_90,
	O_G_INV_180,
	O_G_INV_270,
	O_S_HORIZ,
	O_S_VERT,
	O_S_INV_HORIZ,
	O_S_INV_VERT,
	O_TRI_0,
	O_TRI_90,
	O_TRI_180,
	O_TRI_270
};

const unsigned char CFigure::_offsets[][4][2] =
{
	// O_QUAD
	{
		/*			*/	/*			*/	/*			*/	/*			*/
		/*			*/		{1, 1},			{1, 2},		/*			*/
		/*			*/		{2, 1},			{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	//{
	//	/*	{0, 0},	*/	/*	{0, 1},	*/	/*	{0, 2},	*/	/*	{0, 3},	*/
	//	/*	{1, 0},	*/		{1, 1},			{1, 2},		/*	{1, 3},	*/
	//	/*	{2, 0},	*/		{2, 1},			{2, 2},		/*	{2, 3},	*/
	//	/*	{2, 0},	*/	/*	{2, 1},	*/	/*	{2, 2},	*/	/*	{3, 3},	*/
	//},

	//{
	//	/**/		/**/	/**/		/**/	/**/		/**/	/**/		/**/
	//	/**/		/**/	/**/{1, 1},	/**/	/**/{1, 2},	/**/	/**/		/**/
	//	/**/		/**/	/**/{2, 1},	/**/	/**/{2, 2}	/**/	/**/		/**/
	//	/**/		/**/	/**/		/**/	/**/		/**/	/**/		/**/
	//},

	// O_PALKA_HORIZ
	{
		/*			*/	/*			*/	/*			*/	/*			*/
			{1, 0},			{1, 1},			{1, 2},			{1, 3},
		/*			*/	/*			*/	/*			*/	/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_PALKA_VERT
	{
		/*			*/		{0, 1},		/*			*/	/*			*/
		/*			*/		{1, 1},		/*			*/	/*			*/
		/*			*/		{2, 1},		/*			*/	/*			*/
		/*			*/		{3, 1},		/*			*/	/*			*/
	},

	// O_G_0
	{
		/*			*/		{0, 1},			{0, 2},		/*			*/
		/*			*/		{1, 1},		/*			*/	/*			*/
		/*			*/		{2, 1},		/*			*/	/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_G_90
	{
		/*			*/	/*			*/	/*			*/	/*			*/
			{1, 0},		/*			*/	/*			*/	/*			*/
			{2, 0},			{2, 1},			{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_G_180
	{
		/*			*/	/*			*/		{0, 2},		/*			*/
		/*			*/	/*			*/		{1, 2},		/*			*/
		/*			*/		{2, 1},			{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_G_270
	{
		/*			*/	/*			*/	/*			*/	/*			*/
			{1, 0},			{1, 1},			{1, 2},		/*			*/
		/*			*/	/*			*/		{2, 2},		/*			*/
	},

	// O_G_INV_0
	{
		/*			*/		{0, 1},			{0, 2},		/*			*/
		/*			*/	/*			*/		{1, 2},		/*			*/
		/*			*/	/*			*/		{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_G_INV_90
	{
		/*			*/	/*			*/	/*			*/	/*			*/
			{1, 0},			{1, 1},			{1, 2},		/*			*/
			{2, 0},		/*			*/	/*			*/	/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_G_INV_180
	{
		/*			*/		{0, 1},		/*			*/	/*			*/
		/*			*/		{1, 1},		/*			*/	/*			*/
		/*			*/		{2, 1},			{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_G_INV_270
	{
		/*			*/	/*			*/	/*			*/	/*			*/
		/*			*/	/*			*/		{1, 2},		/*			*/
			{2, 0},			{2, 1},			{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_S_HORIZ
	{
		/*			*/	/*			*/	/*			*/	/*			*/
		/*			*/		{1, 1},			{1, 2},		/*			*/
			{2, 0},			{2, 1},		/*			*/	/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_S_VERT
	{
		/*			*/		{0, 1},		/*			*/	/*			*/
		/*			*/		{1, 1},			{1, 2},		/*			*/
		/*			*/	/*			*/		{2, 2},		/*			*/
	},

	// O_S_INV_HORIZ
	{
		/*			*/	/*			*/	/*			*/	/*			*/
			{1, 0},			{1, 1},		/*			*/	/*			*/
		/*			*/		{2, 1},			{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_S_INV_VERT
	{
		/*			*/	/*			*/		{0, 2},		/*			*/
		/*			*/		{1, 1},			{1, 2},		/*			*/
		/*			*/		{2, 1},		/*			*/	/*			*/
	},

	// O_TRI_0
	{
		/*			*/	/*			*/	/*			*/	/*			*/
		/*			*/		{1, 1},		/*			*/	/*			*/
			{2, 0},			{2, 1},			{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_TRI_90
	{
		/*			*/	/*			*/		{0, 2},		/*			*/
		/*			*/		{1, 1},			{1, 2},		/*			*/
		/*			*/	/*			*/		{2, 2},		/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_TRI_180
	{
		/*			*/	/*			*/	/*			*/	/*			*/
			{1, 0},			{1, 1},			{1, 2},		/*			*/
		/*			*/		{2, 1},		/*			*/	/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},

	// O_TRI_270
	{
		/*			*/		{0, 1},		/*			*/	/*			*/
		/*			*/		{1, 1},			{1, 2},		/*			*/
		/*			*/		{2, 1},		/*			*/	/*			*/
		/*			*/	/*			*/	/*			*/	/*			*/
	},
};

CFigure::CFigure(E_TYPE type, E_ROTATE rotate, unsigned char style, const TField &field):
	_style(style), _field(field), _row(0), _column(3)
{
	switch (type)
	{
	case T_QUAD:
		_offsetsIdx = O_QUAD;
		break;
	case T_PALKA:
		switch (rotate)
		{
		case R_0:
		case R_180:
			_offsetsIdx = O_PALKA_HORIZ;
			break;
		case R_90:
		case R_270:
			_offsetsIdx = O_PALKA_VERT;
			break;
		}
		break;
	case T_G:
		switch (rotate)
		{
		case R_0:
			_offsetsIdx = O_G_0;
			break;
		case R_90:
			_offsetsIdx = O_G_90;
			break;
		case R_180:
			_offsetsIdx = O_G_180;
			break;
		case R_270:
			_offsetsIdx = O_G_270;
			break;
		}
		break;
	case T_G_INV:
		switch (rotate)
		{
		case R_0:
			_offsetsIdx = O_G_INV_0;
			break;
		case R_90:
			_offsetsIdx = O_G_INV_90;
			break;
		case R_180:
			_offsetsIdx = O_G_INV_180;
			break;
		case R_270:
			_offsetsIdx = O_G_INV_270;
			break;
		}
		break;
	case T_S:
		switch (rotate)
		{
		case R_0:
		case R_180:
			_offsetsIdx = O_S_HORIZ;
			break;
		case R_90:
		case R_270:
			_offsetsIdx = O_S_VERT;
			break;
		}
		break;
	case T_S_INV:
		switch (rotate)
		{
		case R_0:
		case R_180:
			_offsetsIdx = O_S_INV_HORIZ;
			break;
		case R_90:
		case R_270:
			_offsetsIdx = O_S_INV_VERT;
			break;
		}
		break;
	case T_TRI:
		switch (rotate)
		{
		case R_0:
			_offsetsIdx = O_TRI_0;
			break;
		case R_90:
			_offsetsIdx = O_TRI_90;
			break;
		case R_180:
			_offsetsIdx = O_TRI_180;
			break;
		case R_270:
			_offsetsIdx = O_TRI_270;
			break;
		}
		break;
	}
}

CFigure &CFigure::operator =(const CFigure &src)
{
	return *new (this) CFigure(src);
}

void CFigure::MoveRight()
{
	++_column;
	if (Collide())
		--_column;
}

void CFigure::MoveLeft()
{
	--_column;
	if (Collide())
		++_column;
}

bool CFigure::MoveDown()
{
	++_row;
	if (Collide())
	{
		--_row;
		return false;
	}
	return true;
}

void CFigure::Rotate(bool cw)
{
	E_OFFSETS new_offsets_idx;
	switch (_offsetsIdx)
	{
	case O_QUAD: return;
	case O_PALKA_HORIZ:
		new_offsets_idx = O_PALKA_VERT;
		break;
	case O_PALKA_VERT:
		new_offsets_idx = O_PALKA_HORIZ;
		break;
	case O_G_0:
		new_offsets_idx = cw ? O_G_270 : O_G_90;
		break;
	case O_G_90:
		new_offsets_idx = cw ? O_G_0 : O_G_180;
		break;
	case O_G_180:
		new_offsets_idx = cw ? O_G_90 : O_G_270;
		break;
	case O_G_270:
		new_offsets_idx = cw ? O_G_180 : O_G_0;
		break;
	case O_G_INV_0:
		new_offsets_idx = cw ? O_G_INV_270 : O_G_INV_90;
		break;
	case O_G_INV_90:
		new_offsets_idx = cw ? O_G_INV_0 : O_G_INV_180;
		break;
	case O_G_INV_180:
		new_offsets_idx = cw ? O_G_INV_90 : O_G_INV_270;
		break;
	case O_G_INV_270:
		new_offsets_idx = cw ? O_G_INV_180 : O_G_INV_0;
		break;
	case O_S_HORIZ:
		new_offsets_idx = O_S_VERT;
		break;
	case O_S_VERT:
		new_offsets_idx = O_S_HORIZ;
		break;
	case O_S_INV_HORIZ:
		new_offsets_idx = O_S_INV_VERT;
		break;
	case O_S_INV_VERT:
		new_offsets_idx = O_S_INV_HORIZ;
		break;
	case O_TRI_0:
		new_offsets_idx = cw ? O_TRI_270 : O_TRI_90;
		break;
	case O_TRI_90:
		new_offsets_idx = cw ? O_TRI_0 : O_TRI_180;
		break;
	case O_TRI_180:
		new_offsets_idx = cw ? O_TRI_90 : O_TRI_270;
		break;
	case O_TRI_270:
		new_offsets_idx = cw ? O_TRI_180 : O_TRI_0;
		break;
	}
	if (!Collide(new_offsets_idx))
		_offsetsIdx = new_offsets_idx;
}

bool CFigure::Collide() const
{
	return Collide(_offsetsIdx);
}

bool CFigure::Collide(E_OFFSETS offsetsIdx) const
{
	const unsigned char (&offsets)[4][2] = _offsets[offsetsIdx];
	for (unsigned i = 0; i < _countof(offsets); i++)
	{
		unsigned char row = _row + offsets[i][0], column = _column + offsets[i][1];
		if (column < 0 || column >= 10 || row >= 20 || row > 0 && _field[row][column] != (unsigned char)~0u)
			return true;
	}
	return false;
}

template<bool relative, class F>
void CFigure::Traverse(F callback) const
{
	const unsigned char (&offsets)[4][2] = _offsets[_offsetsIdx];
	for (unsigned i = 0; i <  _countof(offsets); i++)
	{
		unsigned int row = offsets[i][0], column = offsets[i][1];
		if (relative)
		{
			column += _column;
			row += _row;
		}
		callback(row, column, _style);
	}
}

CTetris::CTetris(unsigned char styleCount, unsigned int initialScore):
	_curFigure(CFigure::E_TYPE(rand() % CFigure::T_LAST), CFigure::E_ROTATE(rand() % CFigure::R_LAST), rand() % styleCount, _field),
	_nextFigure(CFigure::E_TYPE(rand() % CFigure::T_LAST), CFigure::E_ROTATE(rand() % CFigure::R_LAST), rand() % styleCount, _field),
	_time(0), _score(initialScore), _delay(20), _styleCount(styleCount)
{
	memset(_field, ~0, sizeof _field);
}

template<class F>
void CTetris::Traverse(F callback) const
{
	for (unsigned int row = 0; row < 20; row++)
		for (unsigned int column = 0; column < 10; column++)
		{
			unsigned char style = _field[row][column];
			if (style != (unsigned char)~0u)
				callback(row, column, style);
		}
}

inline bool CTetris::CheckRow(unsigned int row) const
{
	for (unsigned int column = 0; column < 10; column++)
		if (_field[row][column] == (unsigned char)~0u)
			return false;
	return true;
}

CTetris::E_EVENT CTetris::Process(bool boost)
{
	_delay *= .9998f;
	if (++_time % max((unsigned int)(boost ? _delay * .1f : _delay), 1) != 0)
		return _curFigure.Collide() ? E_OVERFLOW : E_NONE;
		
	if (!_curFigure.MoveDown())
	{
		// fill field with figure
		class CFiller
		{
		public:
			CFiller(TField &field): _field(field) {}
			void operator ()(int row, int column, unsigned char type)
			{
				_field[row][column] = type;
			}
		private:
			TField &_field;
		};
		_curFigure.Traverse<true>(CFiller(_field));

		// remove filled rows
		// _curFigure.Row() + 4 >= 0 (allways)
		unsigned int rows_removed = 0;
		for (unsigned int row = max(_curFigure.Row(), 0); row < min(_curFigure.Row() + 4, 20); row++)
			if (CheckRow(row))
			{
				rows_removed++;
				memmove(_field[1], _field[0], row * sizeof _field[0]);
				memset(_field[0], ~0, sizeof _field[0]);
			}
		switch (rows_removed)
		{
		case 1:
			_score += 100;
			break;
		case 2:
			_score += 300;
			break;
		case 3:
			_score += 700;
			break;
		case 4:
			_score += 1500;
			break;
		}

		// create new figure
		_curFigure = _nextFigure;
		_nextFigure.~CFigure();
		new (&_nextFigure) CFigure(CFigure::E_TYPE(rand() % CFigure::T_LAST), CFigure::E_ROTATE(rand() % CFigure::R_LAST), rand() % _styleCount, _field);
		return _nextFigure.Collide() ? E_OVERFLOW : E_NEW_FIGURE;
	}
}

template void CFigure::Traverse<false>(CRenderer<false>) const;
template void CFigure::Traverse<true>(CRenderer<false>) const;
template void CTetris::Traverse<>(CRenderer<true>) const;