from math import floor

from clubsandwich.blt.nice_terminal import terminal
from clubsandwich.blt.state import blt_state
from .view import View


class FirstResponderContainerView(View):
  """
  Manages the "first responder" system. The control that receives
  BearLibTerminal events at a given time is the first responder.

  This container view listens for the tab key. When it's pressed, the subview
  tree is walked until another candidate is found, or there are no others.
  That new subview is the new first responder.

  You don't need to create this class yourself. :py:class:`UIScene` makes it
  for you.

  If you want to write a control that handles input, read the source of the
  :py:class:`ButtonView` class.
  """
  def __init__(self, *args, **kwargs):
    self.first_responder = None
    super().__init__(*args, **kwargs)
    self.first_responder = None
    self.find_next_responder()

  @property
  def contains_first_responders(self):
    return True

  def first_responder_traversal(self):
    for subview in self.subviews:
      yield from self._first_responder_traversal(subview)

  def _first_responder_traversal(self, v):
    if v.contains_first_responders:
      # this view may always become the first responder because it will manage
      # inner first responders, but do not try to look inside it.
      yield v
      return
    for subview in v.subviews:
      yield from self._first_responder_traversal(subview)
    yield v

  @property
  def _eligible_first_responders(self):
    return [
      v for v in self.first_responder_traversal()
      if v != self and v.can_become_first_responder]

  def remove_subviews(self, subviews):
    super().remove_subviews(subviews)
    for v in subviews:
      for sv in self.first_responder_traversal(v):
        if sv == self.first_responder:
          self.set_first_responder(None)
          self.find_next_responder()
          return

  def set_first_responder(self, new_value):
    """
    Resign the active first responder and set a new one.
    """
    if self.first_responder:
      self.first_responder.did_resign_first_responder()
      for ancestor in self.first_responder.ancestors:
        ancestor.descendant_did_resign_first_responder(self.first_responder)
    self.first_responder = new_value
    if self.first_responder:
      self.first_responder.did_become_first_responder()
      for ancestor in self.first_responder.ancestors:
        ancestor.descendant_did_become_first_responder(self.first_responder)

  def find_next_responder(self):
    """
    Resign active first responder and switch to the next one.
    """
    existing_responder = self.first_responder or self.leftmost_leaf
    all_responders = self._eligible_first_responders
    try:
      i = all_responders.index(existing_responder)
      if i == len(all_responders) - 1:
        self.set_first_responder(all_responders[0])
      else:
        self.set_first_responder(all_responders[i + 1])
    except ValueError:
      if all_responders:
        self.set_first_responder(all_responders[0])
      else:
        self.set_first_responder(None)

  def find_prev_responder(self):
    """
    Resign active first responder and switch to the previous one.
    """
    existing_responder = self.first_responder or self.leftmost_leaf
    all_responders = self._eligible_first_responders
    try:
      i = all_responders.index(existing_responder)
      if i == 0:
        self.set_first_responder(all_responders[-1])
      else:
        self.set_first_responder(all_responders[i - 1])
    except ValueError:
      if all_responders:
        self.set_first_responder(all_responders[-1])
      else:
        self.set_first_responder(None)

  def terminal_read(self, val):
    handled = self.first_responder and self.first_responder.terminal_read(val)
    if self.first_responder and not handled:
      for v in self.first_responder.ancestors:
        if v == self:
          break
        if v.terminal_read(val):
          return True

    can_resign = (
      not self.first_responder
      or self.first_responder.can_resign_first_responder)
    return self.terminal_read_after_first_responder(val, can_resign)

  def terminal_read_after_first_responder(self, val, can_resign):
    """
    :param int val: Return value of ``terminal_read()``
    :param bool can_resign: ``True`` iff there is an active first responder
                            that can resign

    If writing a custom first responder container view, override this to
    customize input behavior. For example, if writing a list view, you might
    want to use the arrows to change the first responder.
    """
    if can_resign and val == terminal.TK_TAB:
      if blt_state.shift:
        self.find_prev_responder()
        return True
      else:
        self.find_next_responder()
        return True
    return False