#include "tl_board.h"

#include "defines.h"
#include "misc_ui.h"
#include "gpiece.h"

const uint BLOCK_WIDTH  = 15;
const uint BLOCK_HEIGHT = 15;

const uint BOARD_WIDTH  = 10;
const uint BOARD_HEIGHT = 22;

const uint NB_LEDS = 3;
const uint MAX_TO_SEND = 3;

TLBoard::TLBoard(bool graphic, QWidget *parent, const char *name)
: Board(BLOCK_WIDTH, BLOCK_HEIGHT, BOARD_WIDTH, BOARD_HEIGHT,
		graphic, new GiftPool(NB_LEDS, MAX_TO_SEND, parent), parent, name)
{
	info.baseTime         = 1000;
	info.dropDownTime     = 10;
	info.beforeGlueTime   = 10;
	info.afterGlueTime    = 10;
	info.beforeRemoveTime = 150;
	info.afterRemoveTime  = 10;
	info.afterGiftTime    = 10;

	filled.resize(matrix().height());
}

void TLBoard::copy(const GenericTetris &g)
{
	Board::copy(g);

	const TLBoard &t = (const TLBoard &)g;
	filled     = t.filled.copy(); // deep copy
//	addRemoved = t.addRemoved;
}

void TLBoard::init(int seed)
{
	Board::init(seed);
	filled.fill(0);
	addRemoved = 0;
}

bool TLBoard::needRemoving()
{
	for(uint j=0; j<firstClearLine(); j++)
		if ( filled[j]==matrix().width() ) return TRUE;
	return FALSE;
}

void TLBoard::gluePiece()
{
	for(uint k=0; k<currentPiece()->nbBlocks(); k++) {
		int j = currentPiece()->line(k, currentLine());
		filled[j]++;
	}
	Board::gluePiece();
}

void TLBoard::removeLine(uint line)
{
	for (uint i=0; i<matrix().width(); i++) removeBlock(i, line);
	filled[line] = 0;
}

void TLBoard::moveLine(uint srcLine, uint destLine)
{
	for (uint i=0; i<matrix().width(); i++)	moveBlock(i, srcLine, i, destLine);
	filled[destLine] = filled[srcLine];
	filled[srcLine] = 0;
}

void TLBoard::remove()
{
	uint nbFullLines = 0;
	for (uint k=0; k<firstClearLine(); k++) {
		while ( filled[k]==matrix().width() ) {
			removeLine(k);
			nbFullLines++;
			k++;
		}
	}

	updateRemoved(nbRemoved() + nbFullLines);
	addRemoved += nbFullLines;
	
	// Assign score according to level and nb of full lines (gameboy style)
	switch (nbFullLines) {
	 case 0: break;
	 case 1: updateScore( score() + 40   * level() ); break;
	 case 2: updateScore( score() + 100  * level() ); break;
	 case 3: updateScore( score() + 300  * level() ); break;
	 case 4: updateScore( score() + 1200 * level() ); break;
	}

	// If we make a multiplum of ten lines, increase level
	if ( (nbRemoved()/10)!=((nbRemoved()-nbFullLines)/10) )
		updateLevel(level()+1);
}

/*****************************************************************************/
// Multiplayers methods
uint TLBoard::gift()
{
	uint g = (addRemoved>1 ? addRemoved-1 : 0);
	addRemoved = 0;
	return g;
}

bool TLBoard::_putGift(uint nb)
{
	if ( nbClearLines()==0 ) return FALSE;

	// lift all the cases of one line
    // (nbClearLine!=0 --> firstClearLine!=height
	for (uint j=firstClearLine(); j>0; j--) moveLine(j-1, j);

	// fill the emptied low line with garbage :)
	uint _nb = nb;
	uint i;
	do {
		i = randomGarbage.getLong(matrix().width());
		if ( matrix()(i, 0)!=0 ) continue;
		setBlock(i, 0, currentPiece()->garbageBlock());
		_nb--;
	} while ( _nb!=0 );
	filled[0] = nb;
	addToClearLines(-1);
	return TRUE;
}

bool TLBoard::putGift(uint nb)
{
	for (uint k=0; k<nb; k++)
		if ( !_putGift(matrix().width() - k - 1) ) return FALSE;
	return TRUE;
}

/*****************************************************************************/
bool TLBoard::beforeRemove(bool first)
{
	if (first) loop = 0;
	else loop++;

	for(uint j=0; j<firstClearLine(); j++) {
		if ( filled[j]!=matrix().width() ) continue;
		for (uint i=0; i<matrix().width(); i++)
			block(i, j)->toggleLight();
	}

	return ( loop!=3 );
}

void TLBoard::partialMoveLine(uint srcLine, uint destLine, int dec)
{
	for (uint i=0; i<matrix().width(); i++)
		partialMoveBlock(i, srcLine, (srcLine-destLine)*dec);
}

Board::AfterRemoveResult TLBoard::afterRemove(bool doAll, bool first)
{
	int dec = 0;
	bool final = doAll;

	if ( !doAll ) {
		if (first) loop = 0;
		else loop++;

		float fdec = blockHeight();
		switch (loop) {
		case 0: fdec *= 0.3;   break;
		case 1: fdec *= 0.6;   break;
		case 2: fdec *= 1;     break;
		case 3: final = TRUE; break;
		}
		dec = (int)fdec;
	}

	uint nbFullLines = 0, j = 0;
	for (uint k=0; k<firstClearLine(); k++) {
		if ( filled[k]==0 ) {
			nbFullLines++;
			continue;
		}
		if ( j!=k ) {
			if (final) moveLine(k, j);
			else if (_animations) partialMoveLine(k, j, dec);
		}
		j++;
	}
	if (final) addToClearLines(nbFullLines);

	return final ? Done : NeedAfterRemove;
}
