#include "board.h"
#include "board.moc"

#include "gpiece.h"
#include "misc_ui.h"
#include "ai.h"

Board::Board(uint blockWidth, uint blockHeight,
	         uint width, uint height, bool graphic, GiftPool *gp,
			 QWidget *parent, const char *name)
: QFrame(parent, name), GenericTetris(width, height, graphic),
  state(Normal), timer(this), _giftPool(gp), aiEngine(0),
  sequences(0), main(0), _next(0), view(0)
{
	if (graphic) {
		sequences = new SequenceArray(blockWidth, blockHeight);
		main = new BlockInfo(matrix().width(), matrix().height(), *sequences);
		const GPieceInfo *info = Piece::info();
		_next = new BlockInfo(info->maxWidth() + 2,
							  info->maxHeight() + 2, *sequences);
		connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
		setFrameStyle( QFrame::Panel | QFrame::Sunken );
		setBlockInfo(main, _next);
		view = new QCanvasView(main, this);
		view->setGeometry(frameWidth(), frameWidth(),
						  main->width()+5, main->height()+5);
	}
}

Board::~Board()
{
	setBlockInfo(0, 0);
	delete aiEngine;
	delete _next;
	delete main;
	delete sequences;
}

QSize Board::sizeHint() const
{
	if ( !graphic() ) return QSize();
	int fw = 2* frameWidth();
	return QSize(fw + view->width(), fw + view->height());
}

QSizePolicy Board::sizePolicy() const
{
	return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
}

void Board::setType(bool _ai)
{
	ASSERT( graphic() );
	if (_ai) {
		if ( aiEngine==0 ) {
			aiEngine = createAI();
			connect(aiEngine, SIGNAL(rotateLeft()),  SLOT(pRotateLeft()));
			connect(aiEngine, SIGNAL(rotateRight()), SLOT(pRotateRight()));
			connect(aiEngine, SIGNAL(moveLeft()),    SLOT(pMoveLeft()));
			connect(aiEngine, SIGNAL(moveRight()),   SLOT(pMoveRight()));
			connect(aiEngine, SIGNAL(dropDown()),    SLOT(pDropDown()));
		}
	} else {
		delete aiEngine;
		aiEngine = 0;
	}
}

void Board::init(int _seed)
{
	GenericTetris::init(_seed);
    randomGarbage.setSeed(_seed);
}

void Board::start()
{
	ASSERT( graphic() );
	_end  = FALSE;
	state = Normal;
	GenericTetris::start(); // NB: the timer is started by updateLevel !
}

void Board::gameOver()
{
	_end = TRUE;
}

void Board::stop()
{
	ASSERT( graphic() );
	timer.stop();
	if (aiEngine) aiEngine->stop();
}

void Board::pause()
{
	ASSERT( graphic() );
	stop();
	showBoard(FALSE);
}

void Board::showCanvas(QCanvas *c, bool show)
{
	QCanvasItemList l = c->allItems();
	QCanvasItemList::Iterator it;
	for (it=l.begin(); it!=l.end(); ++it) {
		if (show) (*it)->show();
		else (*it)->hide();
	}
	c->update();
}

void Board::showBoard(bool show)
{
	showCanvas(main, show);
	showCanvas(_next, show);
}

void Board::unpause()
{	
	ASSERT( graphic() );
	showBoard(TRUE);
	startTimer();
	if (aiEngine) aiEngine->start(); // eventually restart thinking
}

void Board::updateRemoved(uint newNbRemoved)
{
	GenericTetris::updateRemoved(newNbRemoved);
    emit updateRemovedSignal(newNbRemoved);
}

void Board::updateScore(uint newScore)
{
	GenericTetris::updateScore(newScore);
    emit updateScoreSignal(newScore);
}

void Board::updateLevel(uint newLevel)
{
	GenericTetris::updateLevel(newLevel);
	emit updateLevelSignal(newLevel);
	if ( graphic() ) startTimer();
}

uint Board::blockWidth() const
{
	return sequences->blockWidth();
}

uint Board::blockHeight() const
{
	return sequences->blockHeight();
}

int Board::firstColumnBlock(uint col) const
{
	for (int j=firstClearLine()-1; j>=0; j--)
		if ( matrix()(col, j)!=0 ) return j;
	return -1;
}

void Board::AIConfigChanged()
{
	if (aiEngine) aiEngine->configChanged();
}

/*****************************************************************************/
void Board::pMoveLeft()
{
	if ( state==Normal ) {
		moveLeft();
		main->update();
	}
}

void Board::pMoveRight()
{
	if ( state==Normal ) {
		moveRight();
		main->update();
	}
}

void Board::pOneLineDown()
{
	if ( state==Normal ) {
		oneLineDown();
		main->update();
	}
}

void Board::pRotateLeft()
{
	if ( state==Normal ) {
		rotateLeft();
		main->update();
	}
}

void Board::pRotateRight()
{
	if ( state==Normal ) {
		rotateRight();
		main->update();
	}
}

void Board::pDropDown()
{
	if ( state!=Normal ) return;
	if ( !graphic() ) dropDown();
	else {
		_dropHeight = 0;
		oneLineDown();
		if ( state!=Normal ) return;
		state = DropDown;
		startTimer();
	}
}

void Board::pieceDropped(uint dropHeight)
{
	if ( state==DropDown ) state = Normal;
	else _dropHeight = dropHeight;
	_beforeGlue(TRUE);
}

void Board::_beforeGlue(bool first)
{
	if ( graphic() ) {
		state = (beforeGlue(_dropHeight>=1, first) ? BeforeGlue : Normal);
		if ( state==BeforeGlue ) {
			startTimer();
			return;
		}
	}
	gluePiece();
}

void Board::gluePiece()
{
	GenericTetris::gluePiece();
	_afterGlue();
}

void Board::_afterGlue()
{
	bool b = afterGlue( !graphic() );
	if ( graphic() ) {
		state = (b ? AfterGlue : Normal);
		if ( state==AfterGlue ) {
			computeClearLines();
			startTimer();
			return;
		}
	}
	computeClearLines();
	updateScore(score() + _dropHeight);
	if ( needRemoving() ) {
		_beforeRemove(TRUE);
		return;
	}
	checkGift();
}

void Board::_beforeRemove(bool first)
{
	if ( graphic() ) {
		state = ( beforeRemove(first) ? BeforeRemove : Normal );
		if ( state==BeforeRemove ) {
			startTimer();
			return;
		}
	}
	remove();
	_afterRemove(TRUE);
}

void Board::_afterRemove(bool first)
{
	AfterRemoveResult r = afterRemove(!graphic(), first);
	switch (r) {
	  case Done:            state = Normal;      checkGift();     return;
	  case NeedAfterRemove: state = AfterRemove; startTimer();    return;
	  case NeedRemoving:    _beforeRemove(TRUE);                  return;
	}
}

void Board::checkGift()
{
	if ( graphic() ) {
		if ( _giftPool->pending() ) {
			if ( putGift(_giftPool->take()) ) _afterGift();
			else gameOver();
			return;
		}
	}
	newPiece();
}

void Board::_afterGift()
{
	ASSERT( graphic() );
	if ( afterGift() ) {
		state = AfterGift;
		startTimer();
	} else {
		state = Normal;
		checkGift();
	}
}

void Board::newPiece()
{
	ASSERT( state==Normal );
	GenericTetris::newPiece();
	if ( graphic() ) {
		main->update();
		_next->update();
		startTimer();
		if (aiEngine) aiEngine->launch(this);
		// else : a human player can think by himself ...
	}
}

void Board::timeout()
{
	ASSERT( graphic() );
	switch (state) {
	case DropDown:     _dropHeight++;
	case Normal:       oneLineDown();        break;
	case BeforeGlue:   _beforeGlue(FALSE);   break;
	case AfterGlue:    _afterGlue();         break;
	case BeforeRemove: _beforeRemove(FALSE); break;
	case AfterRemove:  _afterRemove(FALSE);  break;
	case AfterGift:    _afterGift();         break;
	}
	main->update();
}

void Board::startTimer()
{
	ASSERT( graphic() );
	switch (state) {
	case Normal:       timer.start(info.baseTime / (1 + level())); break;
	case DropDown:     timer.start(info.dropDownTime);             break;
	case BeforeGlue:   timer.start(info.beforeGlueTime, TRUE);     break;
	case AfterGlue:    timer.start(info.afterGlueTime, TRUE);      break;
	case BeforeRemove: timer.start(info.beforeRemoveTime, TRUE);   break;
	case AfterRemove:  timer.start(info.afterRemoveTime, TRUE);    break;
	case AfterGift:    timer.start(info.afterGiftTime, TRUE);      break;
	}
}

//-----------------------------------------------------------------------------
bool Board::beforeGlue(bool bump, bool first)
{
	if ( !bump ) return FALSE;
	if (first) loop = 0;
	else loop++;

	float dec = blockHeight();
	switch (loop) {
	case 0:             return TRUE;
	case 1: dec *= -0.2; break;
	case 2: dec *= -0.3; break;
	}
	if (_animations) bumpCurrentPiece((int)dec);

	return ( loop!=3 );
}
