from environment.connect_four.game_state import ConnectFourGameState
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.properties import NumericProperty, StringProperty, BooleanProperty, ListProperty
from kivy.clock import Clock
from environment.errors.unhandled_socket_event import UnhandledSocketEventError
from environment.game_state import GameState
from environment.sockets.client import GameClientSocket
from typing import Any


class GUIApp(App):
  def build(self):
    interface = VisualiserInterface()
    Clock.schedule_interval(interface.catch_game_event, 1.0 / 60.0)
    return interface


class VisualiserInterface(Widget): 
  ply = NumericProperty(-1)
  turn = StringProperty("")
  enable_move_input = BooleanProperty(False)
  terminal = StringProperty("")


  def __init__(self, **kwargs):
    super().__init__(**kwargs)
    self._socket = GameClientSocket()
    self.boardcell = {}
    self._init_board()


  def _update_board(self, state : GameState):
    assert isinstance(state, ConnectFourGameState)
    board = state.board
    for row in range(6):
      for col in range(7):
        cell = self.boardcell[f'{row}{col}']
        label = board[row][col]
        if label == -1:
          cell.text = ''
        elif label == 1:
          cell.color = (1, 1, 1, 1)
          cell.bold = True
          cell.text = 'W'
        else:
          cell.color = (0, 0, 0, 1)
          cell.bold = True
          cell.text = 'B'

        if (row, col) != state.prev_move:
          if (row + col) % 2 == 0:
            cell.background_color = (255/256, 204/256, 151/256, 1)
          else:
            cell.background_color = (221/256, 135/256, 54/256, 1)
        else:
          if (row + col % 2) == 0:
            cell.background_color = (204/256, 208/256, 89/256, 1)
          else:
            cell.background_color = (172/256, 162/256, 28/256, 1)


  def update(self, _):
    pass


  def catch_game_event(self, _):
    event, state = self._socket.recv(noblock=True)
    if event is None:
      return

    if event == "update_interface":
      assert state is not None
      self.ply = state.ply
      self.turn = "White" if state.turn == 0 else "Black"
      if state.score == [1, -1]:
        self.terminal = "White wins"
      elif state.score == [-1, 1]:
        self.terminal = "Black wins"
      elif state.score == [0, 0]:
        self.terminal = "Draw"
      else:
        self.terminal = ""
      self._update_board(state)

    elif event == "get_human_move":
      self.enable_move_input = True
      self.ids.move_input.focus = True

    elif event == "invalid_move":
      self.ids.move_input.foreground_color = (1, 0, 0, 1)
      self.enable_move_input = True
      self.ids.move_input.focus = True

    elif event == "valid_move":
      self.ids.move_input.text = ""
      self.ids.move_input.foreground_color = (0, 0, 0, 1)
      self.enable_move_input = False

    else:
      raise UnhandledSocketEventError(event)


  def on_move_enter(self, move : Any):
    try:
      move_int = int(move)
    except ValueError:
      move_int = -1
    self._socket.send("return_human_move", move_int)
    self.enable_move_input = False


  def _init_board(self):
    board = self.ids.board

    for k in range(board.cols):
      widget = ColoredLabel(
        background_color=(0, 0, 0, 1),
        font_size=60,
        halign='center',
        valign='center',
        text=f"{k+1}"
      )
      board.add_widget(widget)

    for i in range(board.rows-1):
      for j in range(board.cols):
        if (i+j) % 2 == 0:
          widget = ColoredLabel(
            background_color=(255/256, 204/256, 151/256, 1),
            font_size=60,
            halign='center',
            valign='center'
          )
        else:
          widget = ColoredLabel(
            background_color=(221/256, 135/256, 54/256, 1),
            font_size=60,
            halign='center',
            valign='center'
          )
        
        board.add_widget(widget)
        self.boardcell[f'{i}{j}'] = widget
    

class ColoredLabel(Label):
  background_color = ListProperty()
