from logging import getLogger

from externals.device import NetworkScannerDevice, NetworkEscPosPrinterDevice
from externals.device import WindowsApiPrinterDevice
from externals.events import ListenedNonReentrantEvents
from externals.factory import DeviceFactory
from externals.message_handler_builder import CommonMessageHandlerBuilder
from externals.processor import HealthCheckProcessor, PrintProcessor, CashDrawerOpenProcessor, \
    CashDrawerStatusProcessor, PrinterStatusProcessor
from externals.repository import MessageBusRepository
from externals.tokens import ListenedQueueTokens
from messagebus import Event, Message, MessageBus
from messagehandler import MessageHandler
from messageprocessor import DefaultMessageProcessorExecutorFactory, MessageProcessorMessageHandler
from nperipherals_core.interactor import HealthCheckInteractor
from nperipherals_core.interactor.cash_drawer_interactor import CashDrawerStatusInteractor, CashDrawerOpenInteractor
from nperipherals_core.interactor.print_interactor import PrintInteractor, PrinterStatusInteractor
from nperipherals_core.model.device import ReaderDevice, WriterDevice, CashDrawer, Device
from nperipherals_core.service import ReaderDeviceServiceThread
from typing import Dict, Optional, Union

logger = getLogger(__name__)


class HvMessageHandlerBuilder(CommonMessageHandlerBuilder):
    def __init__(self, message_bus, device_configurations):
        # type: (MessageBus, Dict[unicode, unicode]) -> None

        super(HvMessageHandlerBuilder, self).__init__(device_configurations)
        self.message_bus = message_bus

        self.device_service_thread = None  # type: Optional[ReaderDeviceServiceThread]
        self.device = None  # type: Optional[Union[ReaderDevice, WriterDevice, CashDrawer]]

    def build_singletons(self):
        # type: () -> None

        device_factory = DeviceFactory([
            NetworkScannerDevice, WindowsApiPrinterDevice, NetworkEscPosPrinterDevice
        ])
        self.device = device_factory.create_device(self.device_configurations)

        self.device_service_thread = self.device.get_service_thread(MessageBusRepository(self.message_bus))

        if self.device_service_thread:
            self.device_service_thread.start()

    def build_message_handler(self, message=None, event=None):
        # type: (Optional[Message], Optional[Event]) -> MessageHandler

        event_processors = dict()
        msg_processors = dict()

        health_check_interactor = HealthCheckInteractor(self.device_service_thread,
                                                        self.destroy_singletons,
                                                        self.build_singletons)
        health_check_processor = HealthCheckProcessor(health_check_interactor)
        event_processors[ListenedNonReentrantEvents.HealthCheck.name] = health_check_processor

        if self.device:
            if issubclass(type(self.device), Device):
                print_status_interactor = PrinterStatusInteractor(self.device)
                printer_status_processor = PrinterStatusProcessor(print_status_interactor)
                msg_processors[ListenedQueueTokens.TK_PRN_GETSTATUS.value] = printer_status_processor

            if issubclass(type(self.device), WriterDevice):
                print_interactor = PrintInteractor(self.device)
                print_processor = PrintProcessor(print_interactor)
                msg_processors[ListenedQueueTokens.TK_PRN_PRINT.value] = print_processor

            if issubclass(type(self.device), CashDrawer):
                cash_drawer_open_interactor = CashDrawerOpenInteractor(self.device)
                cash_drawer_open_processor = CashDrawerOpenProcessor(cash_drawer_open_interactor)

                cash_drawer_status_interactor = CashDrawerStatusInteractor(self.device)
                cash_drawer_status_processor = CashDrawerStatusProcessor(cash_drawer_status_interactor)

                msg_processors[ListenedQueueTokens.TK_CDRAWER_OPEN.value] = cash_drawer_open_processor
                msg_processors[ListenedQueueTokens.TK_CDRAWER_GETSTATUS.value] = cash_drawer_status_processor

        callbacks = []  # LoggingProcessorCallback(logger) may raise exceptions if data is not compatible with unicode!
        message_handler = MessageProcessorMessageHandler(event_processors,
                                                         msg_processors,
                                                         DefaultMessageProcessorExecutorFactory(callbacks))

        return message_handler

    def destroy_singletons(self):
        # type: () -> None

        if self.device_service_thread:
            self.device_service_thread.stop()
