from .. import user, log, netnode, utils

import ida_kernwin
import idc


class Action(object):
  dialog_cls = None
  reject_handler = None
  accept_handler = None
  finish_handler = None
  submit_handler = None
  response_handler = None
  exception_handler = None

  def __init__(self, dialog_cls=None):
    super(Action, self).__init__()
    if dialog_cls:
      self.dialog_cls = dialog_cls
    self.dialog_obj = None
    self._running = False

  def __repr__(self):
    return "<{}: {}>".format(self.__class__.__name__, self.dialog_cls)

  def running(self):
    return self._running


class IDAAction(Action, ida_kernwin.action_handler_t):
  """Actions are objects registered to IDA's interface and added to the
  rematch menu and toolbar"""

  def __init__(self, *args, **kwargs):
    super(IDAAction, self).__init__(*args, **kwargs)
    self._icon = None

  def __del__(self):
    try:
      super(IDAAction, self).__del__()
      if self._icon:
        ida_kernwin.free_custom_icon(self._icon)
    except AttributeError:
      pass

  def get_name(self):
    return self.name

  def get_id(self):
    return self.get_name().replace('&', '').replace(' ', '_').lower()

  def get_text(self):
    if hasattr(self, 'text'):
      return self.text
    else:
      return self.get_name().replace("&", "")

  def get_shortcut(self):
    if hasattr(self, 'shortcut'):
      return self.shortcut
    else:
      return ""

  def get_tooltip(self):
    if hasattr(self, 'tooltip'):
      return self.tooltip
    else:
      return self.get_text()

  def get_icon(self):
    if not self._icon:
      image_path = utils.get_plugin_path('images', self.get_id() + ".png")
      self._icon = ida_kernwin.py_load_custom_icon_fn(image_path)
    return self._icon

  def get_desc(self):
    return ida_kernwin.action_desc_t(
      self.get_id(),
      self.get_text(),
      self,
      self.get_shortcut(),
      self.get_tooltip(),
      self.get_icon())

  def get_action_group(self):
    if hasattr(self, 'group'):
      return self.group
    else:
      return ""

  def get_action_path(self):
    t = ["Rematch"]

    if self.get_action_group():
      t.append(self.get_action_group())

    t.append(self.get_name())

    return '/'.join(t)

  def register(self):
    r = ida_kernwin.register_action(self.get_desc())
    if not r:
      log('actions').warning("failed registering %s: %s", self, r)
      return
    ida_kernwin.attach_action_to_menu(
        self.get_action_path(),
        self.get_id(),
        ida_kernwin.SETMENU_APP)
    r = ida_kernwin.attach_action_to_toolbar(
        "AnalysisToolBar",
        self.get_id())
    if not r:
      log('actions').warn("registration of %s failed: %s", self, r)

  def update(self, ctx):
    if self.enabled(ctx):
      return ida_kernwin.AST_ENABLE
    else:
      return ida_kernwin.AST_DISABLE

  def activate(self, ctx=None):
    del ctx
    if self.running():
      return
    self._running = True

    if callable(self.dialog_cls):
      try:
        self.dialog_obj = self.dialog_cls(action=self)
        self.dialog_obj.show()
      except Exception:
        log('actions').exception("Exception thrown while showing dialog")
        self._running = False
        self.dialog_obj = None
    else:
      raise NotImplementedError("Activation called on an action class with no "
                                "dialog_cls defined")

  def finish_handler(self, status):
    del status
    log('actions').info("Action finished: %s", self)
    self._running = False


class IdbAction(IDAAction):
  """This action is only available when an idb file is loaded"""
  @staticmethod
  def enabled(ctx):
    del ctx
    return bool(idc.GetIdbPath())


class UnauthAction(IDAAction):
  """This action is only available when a user is logged off"""
  @staticmethod
  def enabled(ctx):
    del ctx
    return not bool(user['is_authenticated'])


class AuthAction(IDAAction):
  """This action is only available when a user is logged in"""
  @staticmethod
  def enabled(ctx):
    del ctx
    return bool(user['is_authenticated'])


class AuthIdbAction(AuthAction, IdbAction):
  """This action is only available when an idb file is loaded and a user is
  logged in"""
  @staticmethod
  def enabled(ctx):
    return AuthAction.enabled(ctx) and IdbAction.enabled(ctx)


class BoundFileAction(AuthIdbAction):
  """This action is only available when a file bound to the remote server is
  loaded"""
  @staticmethod
  def enabled(ctx):
    if not AuthIdbAction.enabled(ctx):
      return False

    return bool(netnode.bound_file_id)


class UnboundFileAction(AuthIdbAction):
  """This action is only available when no file is bound to the remote
  server"""
  @staticmethod
  def enabled(ctx):
    if not AuthIdbAction.enabled(ctx):
      return False

    return not bool(netnode.bound_file_id)