/******************************************************************************
* FILE:   tetris_brick.cc
*         Definition of the class Tetris_brick.
*
* Author: Dana Vrajitoru, IUSB
* Class:  C481 B581 Computer Graphics
******************************************************************************/

#include <stdlib.h>
#include "tetris_brick.h"

// Constructor with a given shape and orientation.
Tetris_brick::Tetris_brick(Brick_type the_shape, int the_orientation)
{ 
  Set_brick(the_shape, the_orientation);
}

// Default constructor that generates a random brick.
Tetris_brick::Tetris_brick()
{
  Set_brick();
}

// Copy constructor
Tetris_brick::Tetris_brick(const Tetris_brick &data)
{
  Set_brick(data);
}

// Destructor: nothing to do.
Tetris_brick::~Tetris_brick()
{;}

// Reinitialize the brick with the shape and orientation.
void Tetris_brick::Set_brick(Brick_type the_shape, int the_orientation)
{
  orientation = the_orientation;
  shape = the_shape;
  Set_initial_pos();
  Reset_bricks();
  switch (shape) {
  case long_shape: // vertical bar
    for (int i=0; i<BRICK_NR; i++)
      bricks[i][1] = i;
    break;
  case l_shape: // {{0,0}, {0,1}, {0,2}, {1,0}} 
    for (int i=0; i<3; i++)
      bricks[i][1] = i;
    bricks[3][0] = 1;
    if (the_orientation)
      Flip_horizontal();
    break;
  case s_shape: // {{0,0}, {1,0}, {1,1}, {2,1}} 
    bricks[1][0] = bricks[2][0] = bricks[2][1] = bricks[3][1] = 1;
    bricks[3][0] = 2;
    if (the_orientation)
      Flip_horizontal();
    break;
  case square_shape: // {{0,0}, {0,1}, {1,0}, {1,1}} 
    bricks[1][1] = bricks[2][0] = bricks[3][0] = bricks[3][1] = 1;
    break;
  case t_shape: // {{0,0}, {1,0}, {2,0}, {1,1}} 
    for (int i=0; i<3; i++)
      bricks[i][0] = i;
    bricks[3][0] = bricks[3][1] = 1;
    break;
  }
} 

// Reinitialize the brick randomly.
void Tetris_brick::Set_brick()
{
  Brick_type the_shape = (Brick_type) (rand() % 5);
  int the_orientation = rand() % 2;
  Set_brick(the_shape, the_orientation);
}

// Copies the data from the second brick.
void Tetris_brick::Set_brick(const Tetris_brick &data)
{
  pos_x = data.pos_x;
  pos_y = data.pos_y;
  orientation = data.orientation;
  shape = data.shape;
  for (int i=0; i<BRICK_NR; i++) {
    bricks[i][0] = data.bricks[i][0];
    bricks[i][1] = data.bricks[i][1];
  }
}

// Sets the intial position of the brick in the game.
void Tetris_brick::Set_initial_pos(int width)
{
  pos_x = width/2;
  pos_y = 0;
}

// Set the position of the brick to the given values.
void Tetris_brick::Set_position(int x, int y)
{
  pos_x = x;
  pos_y = y;
}

// Flip the brick vertically
void Tetris_brick::Flip_vertical()
{
  int max = Max_dim(1);
  for (int i=0; i<BRICK_NR; i++)
    bricks[i][1] = max - bricks[i][1];
}

// Flip the brick horizontally
void Tetris_brick::Flip_horizontal()
{
  int max = Max_dim(0);
  for (int i=0; i<BRICK_NR; i++)
    bricks[i][0] = max - bricks[i][0];
}

// Rotate the brick counterclockwise
void Tetris_brick::Rotate_CCW()
{
  int max = Max_dim(0), x_new, y_new;
  for (int i=0; i<BRICK_NR; i++) {
    x_new = bricks[i][1]; // old y
    y_new = max - bricks[i][0]; // max - old x
    bricks[i][0] = x_new;
    bricks[i][1] = y_new;
  }
}

// Rotate the brick clockwise
void Tetris_brick::Rotate_CW() 
{
  int max = Max_dim(1), x_new, y_new;
  for (int i=0; i<BRICK_NR; i++) {
    x_new = max - bricks[i][1]; // max - old y
    y_new = bricks[i][0]; // old x
    bricks[i][0] = x_new;
    bricks[i][1] = y_new;
  }
}

// Reset all the bricks to the position (0,0)
void Tetris_brick::Reset_bricks()
{
  for (int i=0; i<BRICK_NR; i++)
    for (int j=0; j<2; j++)
      bricks[i][j] = 0;
}

// Compute the max position of a brick in the specified dimension.
int Tetris_brick::Max_dim(int which)
{
  int max = 0;
  for (int i=0; i<BRICK_NR; i++)
    if (bricks[i][which] > max)
      max = bricks[i][which];
  return max;
}

// Checks if the brick fits in the table at the given position.
int Tetris_brick::Check_position(Table_type table, int x, int y)
{
  for (int i=0; i<BRICK_NR; i++)
    if (x+bricks[i][0] >= T_WIDTH || y+bricks[i][1] >= T_HEIGHT)
      return 0;
    else if (table[x+bricks[i][0]][y+bricks[i][1]] != space_cell)
      return 0;
  return 1;
}

// Checks if the brick fits in the table at the given position.
int Tetris_brick::Check_position(Table_type table)
{
  for (int i=0; i<BRICK_NR; i++)
    if (pos_x+bricks[i][0] >= T_WIDTH || pos_y+bricks[i][1] >= T_HEIGHT)
      return 0;
    else if (table[pos_x+bricks[i][0]][pos_y+bricks[i][1]] != space_cell)
      return 0;
  return 1;
}

// Moves the brick down by 1 position if possible. Returns 1 or 0 to
// say if the movement was possible or not.
int Tetris_brick::Move_down(Table_type table)
{
  if (Check_position(table, pos_x, pos_y+1)) {
    pos_y++;
    return 1;
  }
  else
    return 0;
}

// Moves the brick down by 1 position if possible. Returns 1 or 0 to
// say if the movement was possible or not.
int Tetris_brick::Move_left(Table_type table)
{
  if (Check_position(table, pos_x-1, pos_y)) {
    pos_x--;
    return 1;
  }
  else
    return 0;
}

// Moves the brick down by 1 position if possible. Returns 1 or 0 to
// say if the movement was possible or not.
int Tetris_brick::Move_right(Table_type table)
{
  if (Check_position(table, pos_x+1, pos_y)) {
    pos_x++;
    return 1;
  }
  else
    return 0;
}

// Write the brick to the table. It returns the number of rows removed
// from the table.
int Tetris_brick::Write_brick(Table_type table)
{
  int count_rows = 0, j;
  for (int i=0; i<BRICK_NR; i++) {
    table[pos_x+bricks[i][0]][pos_y+bricks[i][1]] = brick_cell;
    if (Check_remove_row(pos_y+bricks[i][1], table)) {
      count_rows++;
      for (j=i+1; j< BRICK_NR; j++)
	if (bricks[j][1] < bricks[i][1])
	  bricks[j][1]++;
    }
  }
  return count_rows;
}
