#ifndef _TTETRIS2D_CPP
#define _TTETRIS2D_CPP
//-------------------------------------------------------
#include "Functions.h"
#include "TTetris2D.h"
//-------------------------------------------------------
TTetris2D::TTetris2D()
{
	std::vector<ITexture*> vTextures;
	TTetris2D(1.0f, 4.0f, 10, 20, TRectF(250, 100, 250, 500), TColor4(124, 0, 0, 255), vTextures);
}
//-------------------------------------------------------
TTetris2D::TTetris2D(float speed, float speedLines, uint width, uint height, const TRectF &drawingRect, const TColor4 &color,
					 const std::vector<ITexture*> &vTextures)
{
	this->_vTextures = vTextures;
	_speedLines = speedLines;
	_indentRemovingLines = 0.0f;
	_score = 0;
	_height = 0;
	_width = 0;
	_loseGame = false;
	_removeLines = false;
	_color = color;
	_drawingRect = TRectF(drawingRect.x - 1, drawingRect.y - 1, drawingRect.width + 2, drawingRect.height + 2);
	this->_vvField = GetRect(width, height);
	this->_speed = speed;
	for(uint i = 0; i < _vvField.size(); ++i)
		for(uint j = 0; j < _vvField[i].size(); ++j)
		{
			_vvField[i][j].SetRect(
									TRectF
										(
											drawingRect.x + i * drawingRect.width / _vvField.size(),
											drawingRect.y + j * drawingRect.height / _vvField[i].size(),
											drawingRect.width / _vvField.size(),
											drawingRect.height / _vvField[i].size()
										)
									);
			_vvField[i][j].SetDisabledColor(color);
			_vvField[i][j].SetExist(false);
		}
	this->CreateFigure();
	this->CreateFigure();
	_systime = GetTickCount();
}
//-------------------------------------------------------
TTetris2D::~TTetris2D()
{

}
//-------------------------------------------------------
void TTetris2D::Draw(IRender2D *pRender2D, IRender *pRender)
{
	pRender->EnableScissor(this->_drawingRect);
	for(uint i = 0; i < _vvField.size(); ++i)
		for(uint j = 0; j < _vvField[i].size(); ++j)
		{
			if(_vvField[i][j].GetExist())
				_vvField[i][j].Draw(pRender2D);
			else
			{
				pRender2D->LineWidth(1);
				pRender2D->DrawRect(_vvField[i][j].GetRect(),TColor4(0x000000,255));
			}
		}
	if( ! _removeLines )
		this->_figure.Draw(pRender2D);
	pRender->DisableScissor();
	this->_nextFigure.Draw(pRender2D);
	if( ! _vvRemovingLines.empty() )
	{
		TRectF rect(0,0,1500,1500);
		pRender->EnableScissor(rect);
		for(uint i = 0; i < _vvRemovingLines.size(); ++i)
			for(uint j = 0; j < _vvRemovingLines[i].size(); ++j)
				_vvRemovingLines[i][j].Draw(pRender2D);
		pRender->DisableScissor();
	}
}
//------------------------------------------------------
void TTetris2D::Game(const ECommand &command)
{
	if(this->IsLoseGame())
		return;
	DWORD curTime = GetTickCount();
	DWORD time = curTime - _systime;
	_systime = curTime;
	if( this->_removeLines )
	{
		_indentRemovingLines += _speedLines * time / 1000;
		if( ! this->IndentLines() )
		{
			this->CreateFigure();
			_height = 0;
		}
		return;
	}
	_height += this->_speed * time / 1000;
	if(this->_height >= this->_vvField[0].size())
		this->_height = (float)this->_vvField[0].size();
	ExecuteCommand(command);
	if( StopFigure())
	{
		// фигура преземлилась
		this->_height = (float)((uint)this->_height);
		this->PutFigure();
		if(this->IsLoseGame())
			return;
		if( ! this->RemoveFillLines())
		{
			this->CreateFigure();
			_height = 0;
		}
	}
	else
		this->_figure.SetPoint(TPoint2(_drawingRect.x + _width * _vvField[0][0].GetRect().width, _drawingRect.y + _height * _vvField[0][0].GetRect().height));
}
//-------------------------------------------------------
bool TTetris2D::RemoveFillLines()
{
	// находим все заполненные линии
	std::vector<uint> vFillLines;
	for(uint j = 0; j < this->_vvField[0].size(); ++j)
	{
		uint i = 0;
		for(; i < this->_vvField.size(); ++i)
			if( ! this->_vvField[i][j].GetExist() )
				break;
		if( i == this->_vvField.size() )
			vFillLines.push_back(j);
	}
	if( vFillLines.empty() )
		return false;
	_removeLines = true;
	// удаляем заполненные линии и формируем массив линий
	for(uint index = 0; index < vFillLines.size(); ++index)
	{
		_vvRemovingLines.resize( _vvRemovingLines.size() + 1 );
		for(uint i = 0; i < this->_vvField.size(); ++i)
		{
			_vvRemovingLines[ _vvRemovingLines.size() - 1 ].push_back( this->_vvField[i][vFillLines[index]] );
			this->_vvField[i][vFillLines[index]].SetExist(false);
		}
	}
	for(uint i = 0; i < _vvRemovingLines.size(); ++i)
		for(uint j = 0; j < _vvRemovingLines[i].size(); ++j)
			_vvRemovingLines[i][j].SetAngleTexture(-90.0f);
	// начисляем очки
	switch(vFillLines.size())
	{
	case 0:
		break;
	case 1:
		this->_score += 100;
		break;
	case 2:
		this->_score += 300;
		break;
	case 3:
		this->_score += 700;
		break;
	case 4:
		this->_score += 1500;
		break;
	default:
		break;
	}
 	return true;
}
//-------------------------------------------------------
bool TTetris2D::IndentLines()
{
	if( _indentRemovingLines > this->_vvField.size())
	{
		_vvRemovingLines.clear();
		this->_removeLines = false;
		// ищем пустые (удалённые) линии
		std::vector<uint> vFillLines;
		for(uint j = 1; j < this->_vvField[0].size(); ++j)
		{
			uint i = 0;
			for(; i < this->_vvField.size(); ++i)
				if( this->_vvField[i][j].GetExist() )
					break;
			if( i == this->_vvField.size() )
				vFillLines.push_back(j);
		}
		// удаляем заполненные линии
		for(uint index = 0; index < vFillLines.size(); ++index)
		{
			// сдвигаем всё, что выше, вниз
			for(int j = (int)vFillLines[index]; j > 0; --j)
			{
				for(uint i = 0; i < this->_vvField.size(); ++i)
				{
					this->_vvField[i][j].SetExist( this->_vvField[i][j - 1].GetExist() );
					this->_vvField[i][j].SetTexture( this->_vvField[i][j - 1].GetTexture() );
				}
			}
			// удаляем верхнюю линию
			for(uint i = 0; i < this->_vvField.size(); ++i)
				this->_vvField[i][0].SetExist(false);
		}
		_indentRemovingLines = 0.0f;
		return false;
	}
	// выезжание удаляемых линий
	if( _removeLines )
	{
		for(uint i = 0; i < _vvRemovingLines.size(); ++i)
			for(uint j = 0; j < _vvRemovingLines[i].size(); ++j)
			{
				_vvRemovingLines[i][j].SetRect(
										TRectF
											(
												_drawingRect.x + (_indentRemovingLines + j) * _vvRemovingLines[i][j].GetRect().width,
												_vvRemovingLines[i][j].GetRect().y,
												_vvRemovingLines[i][j].GetRect().width,
												_vvRemovingLines[i][j].GetRect().height
											)
										);
			}
	}
	return true;
}
//-------------------------------------------------------
void TTetris2D::CreateFigure()
{
	this->_figure = this->_nextFigure;
	std::vector<ITexture*> vTextures;
	if( ! this->_vTextures.empty() )
		for(uint i = 0; i < 4; ++i)
			vTextures.push_back(this->_vTextures[ random(0,this->_vTextures.size()-1) ]);
	uint uiRandomFigure = (uint)random(0, ESimpleFigures_Quantity - 1);
	this->_nextFigure = TFigure2D(
						(ESimpleFigures)uiRandomFigure,
						TRectF(_drawingRect.x, _drawingRect.y, _drawingRect.width / _vvField.size(), _drawingRect.height / _vvField[0].size()),
						TColor4(random(0,255), random(0,255), random(0,255), 255),
						_color,
						vTextures
						);
	if(this->_figure.GetDeltaWidth() < 0)
		this->_width = this->_figure.GetDeltaWidth();
	else
		this->_width = 0;
}
//-------------------------------------------------------
void TTetris2D::PutFigure()
{
	std::vector< std::vector<TSquare> > maskFigure = this->_figure.GetMaskRect();
	for(uint i = 0; i < maskFigure.size(); ++i)
		for(uint j = 0; j < maskFigure[i].size(); ++j)
		{
			if( maskFigure[i][maskFigure[i].size() - 1 - j].GetExist() )
			{
				if((uint)_height + j - maskFigure[i].size() >= this->_vvField[0].size())
				{
					this->_loseGame = true;
					continue;
				}
				if( ! this->_vvField[_width + i][(uint)_height + j - maskFigure[i].size()].GetExist())
				{
					uint originalJ = maskFigure[i].size() - 1 - j;
					this->_vvField[_width + i][(uint)_height + j - maskFigure[i].size()].SetEnabledColor( maskFigure[i][originalJ].GetEnabledColor() );
					this->_vvField[_width + i][(uint)_height + j - maskFigure[i].size()].SetExist(true);
					this->_vvField[_width + i][(uint)_height + j - maskFigure[i].size()].SetTexture( maskFigure[i][originalJ].GetTexture() );
				}
			}
		}
}
//-------------------------------------------------------
bool TTetris2D::StopFigure()
{
	if(_height >= _vvField[0].size())
		return true;
	std::vector< std::vector<TSquare> > maskFigure = this->_figure.GetMaskRect();
	for(uint i = 0; i < maskFigure.size(); ++i)
		for(uint j = 0; j < maskFigure[i].size(); ++j)
		{
			if(! maskFigure[i][j].GetExist() )
				continue;
			int originalJ = (int)((uint)_height + maskFigure[i].size() - j) - (int)maskFigure[i].size();
			if( originalJ >= 0)
				if(this->_vvField[_width + i][originalJ].GetExist())
					return true;
		}
	return false;
}
//-------------------------------------------------------
bool TTetris2D::IsExistStep()
{
	std::vector< std::vector<TSquare> > maskFigure = this->_figure.GetMaskRect();
	for(uint i = 0; i < maskFigure.size(); ++i)
		for(uint j = 0; j < maskFigure[i].size(); ++j)
		{
			if(! maskFigure[i][j].GetExist() )
				continue;
			int originalJ = (int)((uint)_height + maskFigure[i].size() - 1 - j) - (int)maskFigure[i].size();
			int originalI = (int)(_width + i);
			if(originalJ >= 0 && originalI >= 0)
				if(this->_vvField[originalI][originalJ].GetExist())
					return false;
		}
	return true;
}
//-------------------------------------------------------
bool TTetris2D::IsExistRotate()
{
	std::vector< std::vector<TSquare> > maskFigure = this->_figure.GetMaskRect();
	for(uint i = 0; i < maskFigure.size(); ++i)
		for(uint j = 0; j < maskFigure[i].size(); ++j)
		{
			int originalJ = (int)((uint)_height + maskFigure[i].size() - 1 - j) - (int)maskFigure[i].size();
			int originalI = (int)(_width + i);
			if(originalJ >= 0 && originalI >= 0 && (uint)originalI < this->_vvField.size())
			{
				if(this->_vvField[originalI][originalJ].GetExist())
					return false;
			}
			else
				return false;
		}
	return true;
}
//-------------------------------------------------------
bool TTetris2D::IsLoseGame() const
{
	return this->_loseGame;
}
//-------------------------------------------------------
void TTetris2D::ExecuteCommand(const ECommand &command)
{
	switch(command)
	{
	case ECommand_NoCommand:
		break;
	case ECommand_Left:
		if( this->_figure.GetDeltaWidth() < 0)
		{
			if( this->_width != this->_figure.GetDeltaWidth() )
			{
				--this->_width;
				if(!IsExistStep())
					++this->_width;
			}
		}
		else
		{
			if( this->_width != 0 )
			{
				--this->_width;
				if(!IsExistStep())
					++this->_width;
			}
		}
		break;
	case ECommand_Right:
		if( this->_figure.GetDeltaWidth() <= 0)
		{
			if(this->_width + this->_figure.GetWidth() < this->_vvField.size())
			{
				++this->_width;
				if(!IsExistStep())
					--this->_width;
			}
		}
		else
		{
			if(this->_width + this->_figure.GetWidth() - this->_figure.GetDeltaWidth() < this->_vvField.size())
			{
				++this->_width;
				if(!IsExistStep())
					--this->_width;
			}
		}
		break;
	case ECommand_RotateClockwise:
		this->_figure.Rotate(true);
		if( ! IsExistRotate())
			this->_figure.Rotate(false);
		break;
	case ECommand_RotateAnticlockwise:
		this->_figure.Rotate(false);
		if( ! IsExistRotate())
			this->_figure.Rotate(true);
		break;
	case ECommand_Haste:
		++this->_height;
		if(this->_height >= this->_vvField[0].size())
			this->_height = (float)this->_vvField[0].size();
		if( ! IsExistStep() )
			--this->_height;
		break;
	}
}
//-------------------------------------------------------
uint TTetris2D::GetScore() const
{
	return this->_score;
}
//-------------------------------------------------------
TRectF TTetris2D::GetDrawingRect() const
{
	return this->_drawingRect;
}
//-------------------------------------------------------
void TTetris2D::SetDrawingRect(const TRectF &drawingRect)
{
	_drawingRect = drawingRect;
	for(uint i = 0; i < _vvField.size(); ++i)
		for(uint j = 0; j < _vvField[i].size(); ++j)
		{
			_vvField[i][j].SetRect(
									TRectF
										(
											drawingRect.x + i * drawingRect.width / _vvField.size(),
											drawingRect.y + j * drawingRect.height / _vvField[i].size(),
											drawingRect.width / _vvField.size(),
											drawingRect.height / _vvField[i].size()
										)
									);
		}
	this->_figure.SetRect(TRectF(_drawingRect.x + _width * _vvField[0][0].GetRect().width,
									_drawingRect.y + _height * _vvField[0][0].GetRect().height,
									_vvField[0][0].GetRect().width,
									 _vvField[0][0].GetRect().height));
	this->_nextFigure.SetRect(TRectF(_drawingRect.x + _vvField[0][0].GetRect().width * 6,
									_drawingRect.y,
									_vvField[0][0].GetRect().width,
									 _vvField[0][0].GetRect().height));
}
//-------------------------------------------------------
#endif