import os
import time

from uuid import uuid4
from multiprocessing import Queue, Value

from hawkSoar.common.logger import get_logger

from common_helper_files import get_binary_from_file, get_dir_of_file, get_files_in_dir

from hawkSoar.common.process import ExceptionSafeProcess

from hawkSoar.common.filesystem import get_parent_dir
from hawkSoar.common.database import ConnectTo
from hawkSoar.storage.db_interface_view_sync import ViewUpdater

import json


class BasePlugin(object):
    MODULE = "playbook.utilities.base"
    ID = "base-plugin"
    NAME = 'base'
    DEPENDENCIES = []

    def __init__(self, plugin_administrator, config=None, plugin_path=None):
        self.plugin_administrator = plugin_administrator
        self.logger = get_logger()
        self.config = config
        self.incoming_queue = Queue()
        self.outgoing_queue = Queue()
        self.playbooks = {}
        self.Exceptions = []
        self._sync_view(plugin_path)
        self.stop_condition = Value('i', 0)
        self.register_plugin()
        self.start_worker()

    def get_step_by_id(self, playbook_id, step_id):
        i = self.playbooks[playbook_id]
        for w in i['playbook']['workspace']:
            if w['id'] == step_id:
                return w

    def _sync_view(self, plugin_path):
        if plugin_path:
            view_source = self._get_view_file_path(plugin_path)
            if view_source is not None:
                view = get_binary_from_file(view_source)
                with ConnectTo(ViewUpdater, self.config) as connection:
                    connection.update_view(self.NAME, view)

    def _get_view_file_path(self, plugin_path):
        plugin_path = get_parent_dir(get_dir_of_file(plugin_path))
        view_files = get_files_in_dir(os.path.join(plugin_path, 'html'))
        if len(view_files) < 1:
            self.logger.debug(
                '{}: No view available! Generic view will be used.'.format(self.NAME))
            return None
        elif len(view_files) > 1:
            self.logger.warning(
                '{}: Plug-in provides more than one view! \'{}\' is used!'.format(self.NAME, view_files[0]))
        return view_files[0]

    def register_plugin(self):
        return self.plugin_administrator.register_plugin(self.ID, self)

    def get_task(self):
        try:
            # wait 1 sec, or return empty
            return self.incoming_queue.get_nowait()
        except:
            return None

    def start(self):
        raise NotImplementedError

    def set_task(self, task):
        self.incoming_queue.put(task)

    def set_response(self, task):
        return self.outgoing_queue.put(task)

    def add_exception(self, msg):
        self.Exceptions.append(msg)

    def check_exceptions(self):
        # if exceptiosn found, then log out
        return self.Exceptions

    def subscribe_to_playbook_step(self, playbook_id, step_id, module_id):
        # add our subscription message to the outgoing queue
        subscription = {
            "cmd": "task",
            "route": "subscribe",
            "id": str(uuid4()),
            "data": {
                "playbookId": playbook_id,
                "step": step_id,
                "module_id": module_id  # this may be unnecessary
            }
        }
        self.set_response(subscription)

    def _add_playbook_step(self, taskData):
        self.playbooks[taskData['id']] = taskData

    def add_playbook_step(self, playbook, step, stepConfig):
        self.playbooks[playbook['id']] = {
            'id': playbook['id'],
            'playbook': playbook,
            'step': step,
            'config': stepConfig
        }
        taskData = self.playbooks[playbook['id']]
        task = {"cmd": "addPlaybookStep", "data": taskData}
        self.set_task(task)

        self.subscribe_to_playbook_step(playbook['id'], step['id'], self.ID)

    def get_playbook_steps(self):
        return self.playbooks

    # DO ALL THE WORK ON A PER TASK BASIS HERE
    def process_task(self, task):
        raise NotImplementedError

    def kill(self):
        self.stop_condition.value = 1

    def shutdown(self):
        self.stop_condition.value = 1

    def start_worker(self):

        self.process = process = ExceptionSafeProcess(
            target=self.start_process, process_name=self.NAME)
        self.process.start()

    def get_tasks(self):

        if len(self.playbooks) == 0:
            return

        self.logger.debug("Fetching tasks from plugin: %s" % self.NAME)

        request = {
            "cmd": "task",
            "route": "get",
            "id": str(uuid4()),
            "data": {
                "playbookId": None,
                "step": None,
            }
        }

        # self.logger.debug(self.playbooks)

        for id in self.playbooks:
            val = self.playbooks[id]
            request['data']['playbookId'] = id
            request['data']['step'] = val['step']['id']
            # self.logger.debug(request)
            self.outgoing_queue.put(request)

    def start_process(self):
        # get incoming events and throw them in the outgoing bucket
        self.logger.debug("Base plugin run starting...")

        self.logger.debug(" In plugin start_process(%s); Parent PID: %s, My Pid: %s" % (
            self.ID, os.getppid(), os.getpid()))
        self.logger.debug("Plugin: %s starting condition: %d" %
                          (self.ID, self.stop_condition.value))

        while self.stop_condition.value == 0:
            evt = self.get_task()
            if evt != None:
                sts = "true"
            else:
                sts = "false"
            self.logger.debug("Plugin: %s starting condition: %d, task: %s" % (
                self.ID, self.stop_condition.value, sts))
            if not evt:
                # as for a new task?
                self.get_tasks()
                time.sleep(1)
                continue

            self.process_task(evt)

        self.logger.debug("Base[%s] plugin run finishing..." % self.ID)


if __name__ == '__main__':
    pass
