import display
import buttons
import random
import math
import audio
import mch22

class Direction:
  UP = 1
  DOWN = 2
  LEFT = 3
  RIGHT = 4

size = 4

background_color = 0xfaf8ef
foreground_color = 0x776e65
info_font = "roboto_regular12"
title_font = "roboto_black22"
small_font = "fairlight8"
board_dim = 200

display_width = display.width()
display_height = display.height()

board_x = display_width / 2 - board_dim / 2
board_y = display_height / 2 - board_dim / 2 + 15

tile_fonts = ["exo2_bold22", "exo2_bold22", "exo2_bold22", "exo2_bold22", "exo2_bold22", "exo2_bold22", "exo2_bold22", "exo2_bold18", "exo2_bold18", "exo2_bold18", "exo2_bold12"]
tile_spacing = 5
tile_dim = math.floor(((board_dim + tile_spacing / 2) - (size + 1) * tile_spacing) / size)
tile_colors = [0xcdc1b4, 0xeee4da, 0xede0c8, 0xf2b179, 0xf59563, 0xf67c5f, 0xf65e3b, 0xedcf72, 0xedcc61, 0xedc850, 0xedc53f, 0xedc22e]
tile_text_colors = [0xffffff, 0x776e65, 0x776e65, 0xf9f6f2]

score_font = "roboto_regular18"
score_width = 80
score_spacing = 5
score_x = display_width - score_width - score_spacing
score_y = 10
score_padding = 2
score_height = display.getTextHeight("0", score_font) + score_padding
score_text_y = score_y + math.floor(score_padding / 2) - score_padding
score_title_x = score_x + math.floor(score_width / 2 - display.getTextWidth("SCORE", small_font) / 2)

egg = False
over = False
score = 0
board = [0] * (size ** 2) # Powers of 2 to save on space

def new_tile():
  global board

  if not 0 in board:
    return

  while True:
    i = random.randint(0, size ** 2 - 1)
    if board[i] == 0:
      board[i] = random.randint(1, 2)
      break

def reset_board():
  global board, score, egg

  egg = False
  score = 0
  board = [0] * (size ** 2)
  new_tile()
  new_tile()

def draw_score():
  global score
  
  score_text = str(score)
  score_text_x = score_x + math.floor(score_width / 2 - display.getTextWidth(score_text, score_font) / 2)
  display.drawRect(display_width - score_spacing - score_width, score_y, score_width, score_height, True, 0xbbada0)
  display.drawText(score_text_x, score_text_y, score_text, 0xFFFFFF, score_font)

def draw_tile(i):
  global board

  bx = i % size
  by = math.floor(i / size)

  x = board_x + tile_dim * bx + tile_spacing * (bx + 1)
  y = board_y + tile_dim * by + tile_spacing * (by + 1)
  val = board[i]

  background_color = tile_colors[val] if val < len(tile_colors) else tile_colors[len(tile_colors) - 1]

  display.drawRect(x, y, tile_dim, tile_dim, True, background_color)

  if val > 0:
    text = str(2 ** val)
    text_color = tile_text_colors[val] if val < len(tile_text_colors) else tile_text_colors[len(tile_text_colors) - 1]
    font = tile_fonts[val] if val < len(tile_fonts) else tile_fonts[len(tile_fonts) - 1]

    text_x = math.floor(x + tile_dim / 2 - display.getTextWidth(text, font) / 2)
    text_y = math.floor(y + tile_dim / 2 - display.getTextHeight(text, font) / 2)
    display.drawText(text_x, text_y, text, text_color, font)

def draw_board():
  global board

  display.drawRect(board_x, board_y, board_dim, board_dim, True, 0xbbada0)

  for i in range(len(board)):
    draw_tile(i)

def screen_base():
  display.drawFill(background_color)
  display.drawText(10, 5, "2048", foreground_color, title_font)
  display.drawText(score_title_x, 0, "SCORE", foreground_color, small_font)
  draw_score()

def reset_screen():
  screen_base()
  
  display.clearMatrix()
  display.translate(display_width / 2, display_height / 2)
  display.rotate(math.pi/2)
  display.translate(-display_height / 2, -display_width / 2)
  display.drawText(60, 10, "(c) 2137 HY.PN", foreground_color, info_font)
  display.drawText(60, 26, "contact on website above", foreground_color, info_font)
  display.clearMatrix()
  display.translate(display_width / 2, display_height / 2)
  display.rotate(-math.pi/2)
  display.translate(-display_height / 2, -display_width / 2)
  display.drawText(10, 10, "press start to reset", foreground_color, info_font)
  display.drawText(10, 26, "find the easter egg :^)", foreground_color, info_font)
  display.clearMatrix()

  draw_board()
  display.flush()

def defeat():
  global over
  
  audio.play('/apps/python/mch2048/hitHurt.wav', volume=150)
  screen_base()

  over = True
  text = "GAME OVER"
  text_x = math.floor(display_width / 2 - display.getTextWidth(text, title_font) / 2)
  text_y = math.floor(display_height / 2 - display.getTextHeight(text, title_font) / 2)
  display.drawText(text_x, text_y, text, foreground_color, title_font)

  text = "press start to restart"
  text_x = math.floor(display_width / 2 - display.getTextWidth(text, title_font) / 2)
  text_y = math.floor(display_height / 2 - display.getTextHeight(text, title_font) / 2) + 20
  display.drawText(text_x, text_y, text, foreground_color, title_font)
  display.flush()

def easter_egg():
  global egg

  egg = True
  screen_base()
  text = "// TODO: add an easter egg"
  text_x = math.floor(display_width / 2 - display.getTextWidth(text, title_font) / 2)
  text_y = math.floor(display_height / 2 - display.getTextHeight(text, title_font) / 2)
  display.drawText(text_x, text_y, text, foreground_color, title_font)
  display.flush()

def can_move():
  global board

  if 0 in board:
    return True

  for i in range(len(board)):
    if (i + size < len(board) and board[i] == board[i + size]) or (i - size > 0 and board[i] == board[i - size]) or (i % size != 0 and board[i] == board[i - 1]) or (i % size != size - 1 and board[i] == board[i + 1]):
      return True
  
  return False

def rotate(direction, undo = False):
  global board

  if direction == Direction.DOWN:
    return

  copy = board[:]

  for i in range(len(board)):
    x = i % size
    y = math.floor(i / size)
    j = y * size + x

    if (not undo and direction == Direction.LEFT) or (undo and direction == Direction.RIGHT):
      j = (size - 1 - x) * size + y
    elif (not undo and direction == Direction.RIGHT) or (undo and direction == Direction.LEFT):
      j = (x + 1) * size - 1 - y
    elif direction == Direction.UP:
      j = (size - y) * size - 1 - x

    copy[j] = board[i]

  board = copy

def make_move(direction):
  global board, score

  if not can_move():
    defeat()
    return

  rotate(direction)

  make_new_tile = False
  last_merged = None

  for col in range(0, size):
    for row in range(size - 2, -1, -1):
      i = row * size + col
      if board[i] == 0:
        continue

      below = i + size
      merged = False
      fi = None

      while below < len(board) and (board[below] == 0 or (not merged and board[i] == board[below])):
        if below == last_merged:
          break

        make_new_tile = True

        if board[below] != 0:
          merged = True
          score += board[i] ** 2 * 2
          board[below] += 1
        else:
          board[below] = board[i]

        board[i] = 0
        i = below
        fi = below
        below = i + size

      if fi != None and merged:
        last_merged = fi

  rotate(direction, True)

  if make_new_tile:
    audio.play('/apps/python/mch2048/laserShoot.wav', volume=150)
    new_tile()

def reset():
  audio.play('/apps/python/mch2048/blipSelect.wav', volume=150)
  reset_board()
  reset_screen()

def update():
  global over, egg

  if over:
    return

  if egg:
    reset_screen()
  
  draw_score()
  draw_board()
  display.flush()

btn_buffer = ""

def code_check():
  global btn_buffer
  if len(btn_buffer) > 10:
    btn_buffer = btn_buffer[1:]

  if btn_buffer == "UUDDLRLRBA":
    easter_egg()

def on_up_btn(pressed):
  global btn_buffer

  if pressed:
    btn_buffer += "U"
    code_check()
    make_move(Direction.UP)
    update()

def on_down_btn(pressed):
  global btn_buffer

  if pressed:
    btn_buffer += "D"
    code_check()
    make_move(Direction.DOWN)
    update()

def on_left_btn(pressed):
  global btn_buffer

  if pressed:
    btn_buffer += "L"
    code_check()
    make_move(Direction.LEFT)
    update()

def on_right_btn(pressed):
  global btn_buffer

  if pressed:
    btn_buffer += "R"
    code_check()
    make_move(Direction.RIGHT)
    update()

def on_a_btn(pressed):
  global btn_buffer

  if pressed:
    btn_buffer += "A"
    code_check()

def on_b_btn(pressed):
  global btn_buffer

  if pressed:
    btn_buffer += "B"
    code_check()

def on_start_btn(pressed):
  global btn_buffer
  if pressed:
    reset()
    update()

def on_home_btn(pressed):
  if pressed:
    mch22.exit_python()

buttons.attach(buttons.BTN_UP, on_up_btn)
buttons.attach(buttons.BTN_DOWN, on_down_btn)
buttons.attach(buttons.BTN_LEFT, on_left_btn)
buttons.attach(buttons.BTN_RIGHT, on_right_btn)
buttons.attach(buttons.BTN_START, on_start_btn)
buttons.attach(buttons.BTN_A, on_a_btn)
buttons.attach(buttons.BTN_B, on_b_btn)
buttons.attach(buttons.BTN_HOME, on_home_btn)

# Let's begin
reset_board()
reset_screen()