"""Module Data Analytics.

This module consists of DataGenerator class and also the function to generate
bivariate normal distributed datasets.

"""

import paho.mqtt.client as mqtt
import sys
import logging
import statistics
import json

BROKER_ADDRESS='mqtt-broker'
BROKER_PORT=1883
MICRO_SERVICE_NAME = 'data-analytics'

class DataAnalyzer():
    """
    Data Analyzer connects to mqtt broker and waits for new
    input data to calculate KPIs.

    """

    def __init__(self, logger_parent):
        """ Starts the instantiated object with a proper logger """
        
        logger_name = '{}.{}'.format(logger_parent,__name__)
        self.logger = logging.getLogger(logger_name)
        self.client = mqtt.Client(MICRO_SERVICE_NAME)
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_subscribe = self.on_subscribe
        self.client.on_message = self.on_message
        self.topic_callback = dict()

    def on_connect(self, client, userdata, flags, rc):
        self.logger.info('Connected successfully to broker, response code {}'.format(rc))

    def on_disconnect(self, client, userdata, rc):
        if rc != 0:
            self.logger.warning('Connection ended unexpectedly from broker, error code {}'.format(rc))


    def on_subscribe(self, client, userdata, mid, granted_qos):
        
        self.logger.info('successfully subscribed ')

    def on_message(self, client, userdata, message):
        self.logger.info('New message received on topic: {}'.format(message.topic))
        # print(message.payload)
        # load = message.payload
        new_msg = json.loads(message.payload)
        try:
            self.topic_callback[message.topic](new_msg)
        except Exception as err:
            self.logger.error('An error ocurred while hanlding new message of {}: {}'.format(message.topic, err))

    def subscribe(self, topic, callback):
        """ Subscribes to given topic, assigning a callback function that
        handles the received payload

        :topic:     string with the topic to subscribe
        :callback:  function to assign the payload received
        """
        self.topic_callback.update({topic:callback})
        self.client.subscribe(topic)
    
    def standard_kpis(self, payload):
        values = [key['value'] for key in payload]
        result = {
            'mean_result' : statistics.mean(values),
            'median_result' : statistics.median(values),
            'stddev_result' : statistics.stdev(values),
            'name' : payload[0]['name'],
        }
        self.logger.info('mean calculated: {}'.format(statistics.mean(values)))
        self.logger.info('median calculated: {}'.format(statistics.median(values)))
        self.logger.info('stddev calculated: {} \n ======='.format(statistics.stdev(values)))
        self.client.publish(topic='StandardKpiResult', payload=json.dumps(result))
        return

    def sliding_mean(self, payload):
        self.logger.info('calculating sliding mean...')

        current_values = [item['value'] for item in payload['current_drive1_batch']]
        voltage_values = [item['value'] for item in payload['voltage_drive1_batch']]      
        power_batch_sum = sum([current*voltage for current, voltage in zip(current_values,voltage_values)])
        
        power_sliding_mean = round((power_batch_sum/payload['sample_number']),2)
        self.logger.info("sliding mean result: {}\n".format(power_sliding_mean))

        result = {
            'sliding_mean_result' : power_sliding_mean,
            'name' : 'powerdrive1_slidingmean',
        }
        self.client.publish(topic='SlidingMeanResult', payload=json.dumps(result))
        return

    def handle_data(self):        
        """
        Starts the connection to MQTT broker and subscribe to respective
        topics.

        """
        
        self.logger.info('Preparing Mqtt Connection')
        try:
            self.client.connect(BROKER_ADDRESS)
            self.client.loop_start()
            self.subscribe(topic='StandardKpis', callback=self.standard_kpis)
            self.subscribe(topic='SlidingMean', callback=self.sliding_mean)

        except Exception as e:
            self.logger.error(str(e))
