#include "TetriminoManager.h"
#include "SDL.h"

#include <assert.h>

//Load grid data from a 16-bit bitmap
//White pixels get TET_EMPTY, black pixels get 'fill'
BlockColor* loadArray(const char* filename, BlockColor fill);

Tetrimino::Tetrimino(BlockColor* shape)
{
	//Initialize shape
	for (int x = 0; x < TETRIMINO_SIZE; x++)
		for (int y = 0; y < TETRIMINO_SIZE; y++)
			this->shape[x][y] = shape[x + y * TETRIMINO_SIZE];
}

void Tetrimino::draw(SDL_Surface* target, int posx, int posy)
{
	for (int x = 0; x < TETRIMINO_SIZE; x++)
	{
		for (int y = 0; y < TETRIMINO_SIZE; y++)
		{
			SDL_Rect dest;
			dest.x = x * BLOCKIMAGE_SIZE + posx;
			dest.y = y * BLOCKIMAGE_SIZE + posy;
			dest.w = dest.h = BLOCKIMAGE_SIZE;
			SDL_BlitSurface(TetriminoManager::getBlockImage(shape[x][y]), NULL, target, &dest);
		}
	}
}

void Tetrimino::rotateCW()
{
	for (int y = 0; y < 2; y++)
	{
		for (int x = y; x < TETRIMINO_SIZE - y - 1; x++)
		{
			BlockColor temp = shape[x][y];
			shape[x][y] = shape[y][TETRIMINO_SIZE - 1 - x];
			shape[y][TETRIMINO_SIZE - 1 - x] = shape[TETRIMINO_SIZE - 1 - x][TETRIMINO_SIZE - 1 - y];
			shape[TETRIMINO_SIZE - 1 - x][TETRIMINO_SIZE - 1 - y] = shape[TETRIMINO_SIZE - 1 - y][x];
			shape[TETRIMINO_SIZE - 1 - y][x] = temp;
		}
	}
}

int Tetrimino::getLeftmost() const
{
	int left = TETRIMINO_SIZE;
	for (int x = 0; x < TETRIMINO_SIZE; x++)
		for (int y = 0; y < TETRIMINO_SIZE; y++)
			if (shape[x][y] != TET_EMPTY && x < left) left = x;
	return left;
}

int Tetrimino::getRightmost() const
{
	int right = 0;
	for (int x = 0; x < TETRIMINO_SIZE; x++)
		for (int y = 0; y < TETRIMINO_SIZE; y++)
			if (shape[x][y] != TET_EMPTY && x > right) right = x;
	return right;
}

BlockColor Tetrimino::getBlockAt(int x, int y) const
{
	return shape[x][y];
}


void loadArray(const char* filename, BlockColor fill, BlockColor* outArray)
{
	// Ensure array is non-null
	assert(outArray);

	//Load image to build shape from
	SDL_Surface* surf = SDL_LoadBMP(filename);

	//Fill storage based on image data
	for (int p = 0; p < surf->w * surf->h; p++)
	{
		if (((int16_t*)surf->pixels)[p] == 0)
			outArray[p] = fill;
		else
			outArray[p] = TET_EMPTY;
	}
}

// TetrominoManager static variable declarations

list<Tetrimino*> TetriminoManager::upcoming;

SDL_Surface* TetriminoManager::blockImages[TET_COUNT];
BlockColor TetriminoManager::shapes[TET_COUNT][TETRIMINO_SIZE * TETRIMINO_SIZE];

void TetriminoManager::Init()
{
	//Load block images
	blockImages[TET_I] = SDL_LoadBMP("Media/block_i.bmp");
	blockImages[TET_J] = SDL_LoadBMP("Media/block_j.bmp");
	blockImages[TET_L] = SDL_LoadBMP("Media/block_l.bmp");
	blockImages[TET_O] = SDL_LoadBMP("Media/block_o.bmp");
	blockImages[TET_S] = SDL_LoadBMP("Media/block_s.bmp");
	blockImages[TET_T] = SDL_LoadBMP("Media/block_t.bmp");
	blockImages[TET_Z] = SDL_LoadBMP("Media/block_z.bmp");

	//Load tetrimino shapes
	loadArray("Media/data_i.bmp", TET_I, shapes[TET_I]);
	loadArray("Media/data_j.bmp", TET_J, shapes[TET_J]);
	loadArray("Media/data_l.bmp", TET_L, shapes[TET_L]);
	loadArray("Media/data_o.bmp", TET_O, shapes[TET_O]);
	loadArray("Media/data_s.bmp", TET_S, shapes[TET_S]);
	loadArray("Media/data_t.bmp", TET_T, shapes[TET_T]);
	loadArray("Media/data_z.bmp", TET_Z, shapes[TET_Z]);

	//Fill initial tetrimino queue
	for (int c = 0; c < QUEUE_LENGTH; c++)
		queueNewTetrimino();
}

void TetriminoManager::Deinit()
{
	//Clear out the tetrimino queue
	while (!upcoming.empty())
	{
		delete upcoming.front();
		upcoming.pop_front();
	}

	for (int c = 0; c < TET_COUNT; c++)
	{
		//Free textures
		SDL_FreeSurface(blockImages[c]);
	}
}

Tetrimino* TetriminoManager::nextTetrimino()
{
	//Get the front item from the queue
	Tetrimino* ret = upcoming.front();
	upcoming.pop_front();

	//Put in a new one
	queueNewTetrimino();

	return ret;
}

void TetriminoManager::drawQueue(SDL_Surface* surface, int posx, int posy)
{
	int count = 0;
	for (auto it = upcoming.begin(); it != upcoming.end(); it++)
	{
		(*it)->draw(surface, posx, posy + count * (TETRIMINO_SIZE + 1) * BLOCKIMAGE_SIZE);
		count++;
	}
}

SDL_Surface* TetriminoManager::getBlockImage(BlockColor block)
{
	if (block >= TET_COUNT)
		return NULL;
	else
		return blockImages[block];
}

void TetriminoManager::queueNewTetrimino()
{
	upcoming.push_back(new Tetrimino(shapes[rand() % TET_COUNT]));
}