#global dependencies
import abc

#local dependencies
from naeural_core import constants as ct

from naeural_core.business.base import CVPluginExecutor
from naeural_core.business.base.cv_plugin_executor import _CONFIG as BASE_CONFIG

_CONFIG = {
  **BASE_CONFIG,
  
  'RUN_WITHOUT_IMAGE' : True,
  'NON_VIDEO_PLUGIN'  : True,  
  'NO_WITNESS'        : False,

  
  
  'VALIDATION_RULES' : {
    **BASE_CONFIG['VALIDATION_RULES'],
  }
}

class BasicStructuredDataPluginExecutor(CVPluginExecutor):
  def __init__(self, **kwargs):

    self.trained = False
    self._counter = 0

    super(BasicStructuredDataPluginExecutor, self).__init__(**kwargs)
    return

  def startup(self):
    super().startup()
    self._init()
    return

  
  def predict(self, data):
    self._counter += 1

    res = self._predict(data=data)
    dct_res, alert_value = ({}, None) if res is None else res
    # is_alert is generated by self.__alert_helper
    self.add_alerter_observation(alert_value)
    return dct_res
  
  def _init(self):
    return

  ### Section A - methods that should be implemented in subclasses
  if True:

    @abc.abstractmethod
    def _train(self, data):
      raise NotImplementedError()

    @abc.abstractmethod
    def _retrain(self, data):
      raise NotImplementedError()

    @abc.abstractmethod
    def _predict(self, data):
      raise NotImplementedError()
  #endif

  ### Section B - methods overwritten from parent
  if True:
    def _process(self):
      train_data = self.dataapi_specific_input_init_data()
      if train_data is not None:
        if not self.trained:
          self.P("Training {}...".format(self.__class__.__name__), color='y')
          self._train(train_data)
          self.P("Done training {}.".format(self.__class__.__name__), color='y')
        else:
          self.P("RE-Training {}...".format(self.__class__.__name__), color='y')
          self._retrain(train_data)
          self.P("Done retraining {}.".format(self.__class__.__name__), color='y')
        #endif
      #endif

      step_data = self.dataapi_struct_data()

      dct_result = self.predict(data=step_data)

      img = None if self._instance_config.get(ct.NO_WITNESS, True) else self.get_witness_image()

      payload = self._create_payload(
        trained=self.trained,
        img=img,
        struct_data_pass_counter=self._counter,
        is_in_alert=self.alerter_is_alert(),
        is_alert=self.alerter_is_new_alert(),
        msg='{} : {}'.format(self.__class__.__name__, self.__version__),
        **dct_result,
      )

      return payload

    def _post_process(self):

      if self._payload is None:
        ### TODO: maybe remove
        if self.cfg_debug_payloads and self.need_refresh():  # if in "demo mode"
          self._payload = self._create_payload(
            img=self.get_witness_image()
          )
          self._add_alert_info_to_payload(self._payload)
        #endif
      #endif

      return
  #endif
