import time, urandom, system, orientation, display, buttons

orientation.portrait()

display.drawFill(0x000000)
display.flush()
display.drawFill(0xFFFFFF)
display.flush()
display.drawFill(0x000000)
display.flush()
display.drawFill(0xFFFFFF)
display.flush()

class Tetris:
	def __init__(self):
		self.font = "roboto_regular18"
		self.game_step_time = 500

		# nr of unique rotations per piece
		self.piece_rotations = [1, 2, 2, 2, 4, 4, 4]

		# Piece data: [piece_nr * 32 + rot_nr * 8 + brick_nr * 2 + j]
		# with rot_nr between 0 and 4
		# with the brick number between 0 and 4
		# and j == 0 for X coord, j == 1 for Y coord
		self.piece_data = [
			# square block
			0, 0, -1, 0, -1, -1, 0, -1, 
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,

			# line block
			0, 0, -2, 0, -1, 0, 1, 0,
			0, 0, 0, 1, 0, -1, 0, -2,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,

			# S-block
			0, 0, -1, -1, 0, -1, 1, 0, 
			0, 0, 0, 1, 1, 0, 1, -1, 
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,

			# Z-block
			0, 0, -1, 0, 0, -1, 1, -1, 
			0, 0, 1, 1, 1, 0, 0, -1, 
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,

			# L-block
			0, 0, -1, 0, -1, -1, 1, 0, 
			0, 0, 0, 1, 0, -1, 1, -1, 
			0, 0, -1, 0, 1, 0, 1, 1, 
			0, 0, -1, 1, 0, 1, 0, -1, 

			# J-block
			0, 0, -1, 0, 1, 0, 1, -1, 
			0, 0, 0, 1, 0, -1, 1, 1, 
			0, 0, -1, 1, -1, 0, 1, 0, 
			0, 0, 0, 1, 0, -1, -1, -1, 

			# T-block
			0, 0, -1, 0, 0, -1, 1, 0,  
			0, 0, 0, 1, 0, -1, 1, 0, 
			0, 0, -1, 0, 0, 1, 1, 0, 
			0, 0, -1, 0, 0, 1, 0, -1
		]

		display.drawFill(0xFFFFFF)
		self.draw_field_lines()
		display.flush()

		buttons.attach(buttons.BTN_UP,lambda pressed: self.btn_up(pressed))
		buttons.attach(buttons.BTN_DOWN,lambda pressed: self.btn_down(pressed))
		buttons.attach(buttons.BTN_LEFT,lambda pressed: self.btn_left(pressed))
		buttons.attach(buttons.BTN_RIGHT,lambda pressed: self.btn_right(pressed))
		buttons.attach(buttons.BTN_SELECT,lambda pressed: self.btn_select(pressed))
		buttons.attach(buttons.BTN_START,lambda pressed: self.btn_start(pressed))
		buttons.attach(buttons.BTN_A,lambda pressed: self.btn_a(pressed))
		buttons.attach(buttons.BTN_B,lambda pressed: self.btn_b(pressed))
	
		self.game_init()
		self.draw_updated_score()

		while True:
			self.game_update()
			self.draw()

	def game_init(self):
		# Init game state
		self.spawn_new_piece()
		self.field = [[0 for i in range(10)] for j in range(20)]
		self.last_update = time.ticks_ms()
		self.score = 0


	def spawn_new_piece(self):
		self.piece_type = urandom.getrandbits(8) % 7
		self.piece_x = 4
		self.piece_y = 0
		self.piece_rot = 0


	def game_update(self):
		cur_ticks = time.ticks_ms()
		if cur_ticks - self.last_update > self.game_step_time:
			# Move piece down
			self.lower_piece()
			self.last_update = cur_ticks

	def rotate_piece(self):
		self.piece_rot += 1
		if self.piece_rot >= self.piece_rotations[self.piece_type]:
			self.piece_rot = 0

	def is_side_collision(self):
		for square in range(4):
			x_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 0]
			y_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 1]

			abs_x = self.piece_x + x_off
			abs_y = self.piece_y + y_off

			# collision with walls
			if abs_x < 0 or abs_x >= 10:
				return True

			# collision with field blocks
			if self.field[abs_y][abs_x]:
				return True

		return False


	def move_left(self):
		self.piece_x -= 1

		# check collision with walls
		if self.is_side_collision():
			self.piece_x += 1

	def move_right(self):
		self.piece_x += 1

		# check collision with walls
		if self.is_side_collision():
			self.piece_x -= 1


	def lower_piece(self):
		self.piece_y += 1

		# check for collisions
		collision = False
		for square in range(4):
			x_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 0]
			y_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 1]

			abs_x = self.piece_x + x_off
			abs_y = self.piece_y + y_off

			try:
				if abs_y >= 20 or self.field[abs_y][abs_x]:
					collision = True
					break
			except:
				print("Out-of-bounds?!")
				pass

		if collision:
			# if at the top, game over
			if self.piece_y == 1:
				self.draw_game_over()
				time.sleep(10)
				self.game_init()

			# add to field
			self.piece_y -= 1
			for square in range(4):
				x_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 0]
				y_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 1]

				abs_x = self.piece_x + x_off
				abs_y = self.piece_y + y_off

				self.field[abs_y][abs_x] = True

			# check for line clears
			self.check_for_filled_lines()

			# Spawn new piece
			self.spawn_new_piece()


	def check_for_filled_lines(self):
		for row in range(20):
			fill_count = 0
			for column in range(10):
				fill_count += self.field[row][column]
			if fill_count == 10:
				# Increase score
				self.score += 1
				self.draw_updated_score()

				# Remove line
				for row2 in range(row, 0, -1):
					for column2 in range(10):
						self.field[row2][column2] = self.field[row2-1][column2]

	def draw(self):
		self.draw_field()
		self.draw_current_piece()
		display.flush(display.FLAG_LUT_FASTEST)

	def draw_updated_score(self):
		display.drawRect(0, display.height()-20, display.width(), 20, True, 0xFFFFFF)
		display.drawText(10, display.height()-20, "Score: {:04d}".format(self.score), 0x000000, self.font)

	def draw_game_over(self):
		display.drawFill(0xFFFFFF)
		display.drawText(0, 0, "GAME OVER :(")
		display.flush()

	def draw_field_lines(self):
		display.drawRect(10, 10, 100, 200, False, 0x000000)
		#for column in range(10):
		#	display.drawLine(10 + 10 * column, 10, 10 + 10 * column, 210, 0x000000)
		#for row in range(20):
		#	display.drawLine(10, 10 + 10 * row, 110, 10 + 10 * row, 0x000000)

	def draw_field(self):
		for column in range(10):
			for row in range(20):
				self.draw_square(row, column, self.field[row][column])
		self.draw_field_lines()

	def draw_current_piece(self):
		for square in range(4):
			# [piece_nr * 32 + rot_nr * 8 + brick_nr * 2 + j]
			x_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 0]
			y_off = self.piece_data[self.piece_type * 32 + self.piece_rot * 8 + square * 2 + 1]

			abs_x = self.piece_x + x_off
			abs_y = self.piece_y + y_off

			self.draw_square(abs_y, abs_x, True)


	def draw_square(self, row, column, filled=True):
		if row < 0 or column < 0 or row >= 20 or column >= 10:
			return
		display.drawRect(10 + 10 * column, 200 - 10 * (19-row), 10, 10, False, 0x000000 if filled else 0xFFFFFF)

	def btn_a(self,pressed):
		if pressed:
			print("Rotating piece")
			self.rotate_piece()

	def btn_b(self,pressed):
		if pressed:
			print("Rotating piece")
			self.rotate_piece()

	def btn_left(self,pressed):
		if pressed:
			print("Moving piece left")
			self.move_left()

	def btn_right(self,pressed):
		if pressed:
			print("Moving piece right")
			self.move_right()

	def btn_down(self,pressed):
		if pressed:
			print("Moving piece down")
			self.lower_piece()

	def btn_up(self,pressed):
		if pressed:
			print("Rotating piece")
			self.rotate_piece()

	def btn_start(self,pressed):
		if pressed:
			print("Returning to homescreen")
			system.home()

	def btn_select(self,pressed):
		if pressed:
			print("Resetting game")
			self.game_init()


tetris = Tetris()