#include "gpiece.h"

//-----------------------------------------------------------------------------
GPieceInfo::GPieceInfo()
{
	Piece::setPieceInfo(this);	
}

uint GPieceInfo::maxWidth() const
{
	int min, max;
	uint w = 0;
	for (uint n=0; n<nbForms(); n++) {
		min = max = i(n)[0];
		for (uint k=0; k<nbBlocks(); k++) {
			int tmp = i(n)[k];
			if ( tmp>max ) max = tmp;
			else if ( tmp<min ) min = tmp;
		}
		w = (uint)QMAX(w, (uint)max-min);	
	}
	return w;
}

uint GPieceInfo::maxHeight() const
{
	int min, max;
	uint h = 0;
	for (uint n=0; n<nbForms(); n++) {
		min = max = j(n)[0];
		for (uint k=0; k<nbBlocks(); k++) {
			int tmp = j(n)[k];
			if ( tmp>max ) max = tmp;
			else if ( tmp<min ) min = tmp;
		}
		h = (uint)QMAX(h, (uint)max-min);	
	}
	return h;
}

//-----------------------------------------------------------------------------
SequenceArray::SequenceArray(uint bw, uint bh)
: _blockWidth(bw), _blockHeight(bh)
{
	const GPieceInfo *pinfo = Piece::info();
	resize(pinfo->nbBlockTypes());
	QList<QPixmap> pixmaps;
	pixmaps.setAutoDelete(TRUE);
	QList<QPoint> points;
	points.setAutoDelete(TRUE);
	for (uint i=0; i<size(); i++) {
		for (uint k=0; k<2; k++)
			for (uint j=0; j<pinfo->nbBlockModes(); j++) {
				points.append(new QPoint(0, 0));
				pixmaps.append(pinfo->drawPixmap(_blockWidth, _blockHeight,
												 i, j, k==1));
			}
		at(i) = new QCanvasPixmapArray(pixmaps, points);
		pixmaps.clear();
		points.clear();
	}
}

SequenceArray::~SequenceArray()
{
	for (uint i=0; i<size(); i++) delete at(i);
}

//-----------------------------------------------------------------------------
BlockInfo::BlockInfo(uint w, uint h, const SequenceArray &s)
: _sequences(s)
{
	// #### set tuning ??
	resize(w*s.blockWidth(), h*s.blockHeight());
	setBackgroundColor(black);
}

//-----------------------------------------------------------------------------
Block::Block()
: sprite(0)
{}

Block::Block(uint value)
: _value(value), sprite(0)
{}

Block::~Block()
{
	delete sprite;
}

void Block::setValue(uint value, BlockInfo *binfo)
{
	_value = value;
	if (binfo) {
		QCanvasPixmapArray *seq = binfo->sequences()[value];
		if (sprite) sprite->setSequence(seq);
		else {
			sprite = new QCanvasSprite(seq, binfo);
			sprite->setZ(0);
		}
	}
}

void Block::toggleLight()
{
	const GPieceInfo *pinfo = Piece::info();
	uint f = sprite->frame() + pinfo->nbBlockModes()
		* (sprite->frame()>=(int)pinfo->nbBlockModes() ? -1 : 1);
	sprite->setFrame(f);
}

void Block::setMode(uint mode)
{
	sprite->setFrame(mode);
}

void Block::move(int x, int y)
{
	sprite->move(x, y);
}

bool Block::isGarbage() const
{
	return ( _value==Piece::info()->garbageType() );
}

void Block::show(bool s)
{
	if (s) sprite->show();
	else sprite->hide();
}

//-----------------------------------------------------------------------------
const GPieceInfo *Piece::_info = 0;

Piece::Piece()
: binfo(0)
{
	blocks.resize(_info->nbBlocks());
	for (uint i=0; i<blocks.size(); i++) blocks[i].b = new Block;
}

Piece::~Piece()
{
	clean();
}

void Piece::clean()
{
	for (uint i=0; i<blocks.size(); i++) delete blocks[i].b;
}

void Piece::rotate(bool left, int x, int y)
{
    for (uint i=0; i<blocks.size(); i++) {
        int tmp = blocks[i].i;
		blocks[i].i = blocks[i].j * (left ?  1 : -1);
		blocks[i].j = tmp         * (left ? -1 :  1);
		if (binfo) moveBlock(i, x, y);
    }
}

int Piece::minX() const
{
    int min = blocks[0].i;
    for(uint i=1; i<blocks.size(); i++) min = QMIN(min, blocks[i].i);
    return min;
}

int Piece::maxX() const
{
    int max = blocks[0].i;
    for(uint i=1; i<blocks.size(); i++) max = QMAX(max, blocks[i].i);
    return max;
}

int Piece::minY() const
{
    int min = blocks[0].j;
    for(uint i=1; i<blocks.size(); i++) min = QMIN(min, blocks[i].j);
    return min;
}

int Piece::maxY() const
{
    int max = blocks[0].j;
    for(uint i=1; i<blocks.size(); i++) max = QMAX(max, blocks[i].j);
    return max;
}

void Piece::copy(const Piece *p, bool toNonGraphic)
{
	for (uint i=0; i<blocks.size(); i++) {
		blocks[i].b->setValue(p->blocks[i].b->value(),
							  (toNonGraphic ? 0 : binfo));
		blocks[i].i = p->blocks[i].i;
		blocks[i].j = p->blocks[i].j;
	}
	type   = p->type;
	random = p->random;
}

void Piece::generateNext(int _type)
{
	ASSERT(binfo); // ie graphic
	type = (_type==-1 ? random->getLong(_info->nbTypes()) : (uint)_type );

	for (uint i=0; i<blocks.size(); i++) {
		uint f = _info->form(type);
		blocks[i].i = _info->i(f)[i];
		blocks[i].j = _info->j(f)[i];
		blocks[i].b->setValue(_info->value(type, i), binfo);
	}
}

void Piece::moveCenter()
{
	uint bw = binfo->sequences().blockWidth();
	int x = (binfo->width() - width()*bw)/2 - minX()*bw;
	uint bh = binfo->sequences().blockHeight();
	int y = (binfo->height() - height()*bh)/2 - minY()*bh;
	move(x, y);
}

void Piece::move(int x, int y)
{
	for (uint i=0; i<blocks.size(); i++) moveBlock(i, x, y);
}

void Piece::moveBlock(uint i, int x, int y)
{
	blocks[i].b->move(x + toX(blocks[i].i), y + toY(blocks[i].j));
}

int Piece::toX(uint col) const
{
	return col*binfo->sequences().blockWidth();
}

int Piece::toY(uint line) const
{
	return line*binfo->sequences().blockHeight();
}

Block *Piece::garbageBlock() const
{
	Block *b = new Block;
	b->setValue(_info->garbageType(), binfo);
	return b;
}

Block *Piece::takeBlock(uint i)
{
	Block *b = blocks[i].b;
	blocks[i].b = new Block;
	return b;
}

void Piece::show(bool show)
{
	for (uint i=0; i<blocks.size(); i++)
		blocks[i].b->show(show);
}
	
void Piece::setBlockInfo(BlockInfo *bi)
{
	binfo = bi;
	if ( bi==0 ) { // before destruction
		clean();
		blocks.resize(0);
	}
}
