# Copyright 2016 VMware, Inc.  All rights reserved. -- VMware Confidential
#
''' Module containing message dispatching classes.
'''
import logging

from status_reporting_sdk.progressReporter import ProgressReporter
from status_reporting_sdk.messageReporter import MessageReporter
from status_reporting_sdk.execution_settings import getCoreExecutionSettings

logger = logging.getLogger(__name__)

class MessageType(object):
    ''' Defines supported message types
    '''
    UNKNOWN = 0
    CONFIGURATION_CHANGE = 1
    PROGRESS_REPORTING = 2
    MESSAGE_REPORTING = 3
    QUESTION_REPORTING = 4
    COMPONENT_RESULT = 5

class ReportingFactory(object):
    ''' Provides several reporters that can report messages, questions and
        progress data. All share the queue provided and use same
        identifier to identify them.
    '''
    def __init__(self, reportQueue, identifier, reportErrors=True):
        '''
        @param reportQueue: A queue to report any messages, questions and
            progress reports.
        @type reportQueue: multiprocessor.Queue

        @param identifier: Identifier to identify messages from the
            MessageDispatcher
        @type identifier: str

        @param reportErrors: Report errors from the execution. If it is False,
            just will log them
        @type reportErrors: bool
        '''
        self.reportErrors = reportErrors
        self.reportQueue = reportQueue
        self._configure(reportQueue, identifier)

    def _configure(self, reportQueue, identifier):
        if not reportQueue:
            logger.warn('Reporting queue is none, not configuring reporting via queue.')
        self.progressReporter = _ProgeressReporter(reportQueue, identifier, self.reportErrors)
        self.messageReporter = _MessageReporter(reportQueue, identifier, self.reportErrors)
        self.configurationReporter = _ConfigrationReporter(reportQueue, identifier)

class _ProgeressReporter(ProgressReporter):
    ''' Wrapper class from common-sdk ProgressReporter, gives the ability
        to push to queues
    '''
    def __init__(self, queue, identifier, reportErrors):
        messageMetadata = _generateMetadata(MessageType.PROGRESS_REPORTING,
                                            identifier)
        if queue:
            sendResult = lambda progressData: queue.put(_generateMessage(messageMetadata, progressData))
        else:
            sendResult = lambda progressData: None
        settings = getCoreExecutionSettings()
        settings.componentName = identifier
        self.reportErrors = reportErrors
        super(_ProgeressReporter, self).__init__(setting=settings,
                                                 updateFun=sendResult)

    def failure(self, errInfo):
        ''' see @ProgressReporter.failure
        '''
        if self.reportErrors:
            super(_ProgeressReporter, self).failure(errInfo)
        else:
            logger.warn('Error reporting is turned off for this execution. '
                        'Got error %s', errInfo)

class _MessageReporter(MessageReporter):
    ''' Wrapper class from common-sdk MessageReporter, gives the ability
        to push to queues
    '''
    def __init__(self, queue, identifier, reportErrors):
        messageMetadata = _generateMetadata(MessageType.MESSAGE_REPORTING,
                                            identifier)
        if queue:
            sendResult = lambda message, sevirity: queue.put(_generateMessage(messageMetadata, [message, sevirity]))
        else:
            sendResult = lambda message, sevirity: None
        settings = getCoreExecutionSettings()
        settings.componentName = identifier
        self.reportErrors = reportErrors
        super(_MessageReporter, self).__init__(setting=settings,
                                               updateFun=sendResult)

    def _postError(self, message, resolution=None, problemId=None):
        ''' see @MessageReporter._postError
        '''
        if self.reportErrors:
            super(_MessageReporter, self)._postError(message, resolution, problemId)
        else:
            logger.warn('Error reporting is turned off for this execution. '
                        'Got error %s %s %s', message, resolution, problemId)

class _ConfigrationReporter(object):
    ''' Reporter that gives the ability to push configuration messages to a queue
    '''
    def __init__(self, queue, identifier):
        self.queue = queue
        self.messageMetadata = _generateMetadata(MessageType.CONFIGURATION_CHANGE,
                                                 identifier)

    def requestConfigurationChange(self, newConfigRequest):
        ''' Send a request to change the configuration of the one that consumes
        messages via the queue provided. The consumer is the one that decides if
        he is going to honor the request.

        @param newConfigRequest: The configuration request, can be anything, the
            consumer will have to know how to process it.
        @type newConfigRequest: any serializable type
        '''
        if self.queue:
            self.queue.put(_generateMessage(self.messageMetadata,newConfigRequest))

def _generateMessage(metadata, payload):
    return {'metadata' : metadata, 'payload' : payload}

def _generateMetadata(typ, identifier):
    return {'type' : typ, 'identifier' : identifier}

class ImpersonatableReportingFactory(ReportingFactory):
    ''' Implementation of ReportingFactory that allows to change identifier of
        reporters to impersonate another ReportingFactory. For example if
        framework wants to report messages on behalf of component.
    '''
    def __init__(self, reportQueue, identifier):
        '''
        @param reportQueue: A queue to report any messages, questions and
            progress reports.
        @type reportQueue: multiprocessor.Queue

        @param identifier: Identifier to identify messages from the
            MessageDispatcher
        @type identifier: str
        '''
        self.reportQueue = reportQueue
        super(ImpersonatableReportingFactory, self).__init__(reportQueue,
                                                             identifier)

    def impersonate(self, identifier):
        '''Change the identifier used by the MessageDispatcher and
            the reporters it provides
        '''
        self._configure(self.reportQueue, identifier)
