import json
import time
import unittest
from mock import patch

from cloudbrain.modules.sources.openbci import OpenBCISource
from cloudbrain.publishers.rabbitmq import PikaPublisher
from cloudbrain.connectors.openbci import OpenBCIConnector, OpenBCISample



class MockSerial(object):
    """
    Patch class serial.Serial
    """


    def __init__(self, port, baud):
        pass


    def write(self, text):
        pass


    def read(self):
        return 'mock'


    def close(self):
        pass


    def inWaiting(self):
        return False



def mock_start(self, callback_functions):
    """
    Patch method OpenBCIConnector.start

    :param callback_functions: (dict)
      E.g: {metric_0: callback_0, ...,  metric_N: callback_N}
      where 'metric_X' is a string and 'callback_X' is a function that takes an
      Array as argument.
    """

    packet_id = 0
    channel_data = [i for i in range(8)]
    aux_data = []
    timestamp = int(time.time() * 1000000) # in microseconds
    sample = OpenBCISample(packet_id, channel_data, aux_data, timestamp)
    for (metric, callback_function) in callback_functions.items():
        callback_function(sample)



class MockChannel(object):
    def __init__(self):
        self.published_count = 0
        self.message_published = None


    def exchange_declare(self, exchange, type):
        pass


    def basic_publish(self, exchange, routing_key, body, properties):
        self.published_count += 1
        self.message_published = body
        print("Message count: %s" % self.published_count)
        print("Message body: %s" % body)



class MockBlockingConnection(object):
    """
    Patch pika's BlockingConnection
    """


    def __init__(self, params):
        pass


    def close(self):
        pass


    def channel(self):
        return MockChannel()



class OpenBCITest(unittest.TestCase):
    def setUp(self):
        self.device = 'openbci'
        self.user = "mock"

        # OpenBCI config
        self.port = None
        self.baud = 0
        self.filter_data = False

        # metric info
        self.metric_name = 'eeg'
        self.num_channels = 8
        self.buffer_size = 2

        # rmq info
        self.rabbitmq_address = 'mock_rabbitmq'
        self.rabbitmq_user = 'mock_user'
        self.rabbitmq_pwd = 'mock_pwd'

        self.base_routing_key = '%s:%s' % (self.user, self.device)

        self.message = {'timestamp': 100}
        for i in range(self.num_channels):
            self.message['channel_%s' % i] = i


    def validate_start_method(self, sample):
        self.assertEqual(sample.channel_data, [i for i in range(self.num_channels)])
        print("OpenBCI started: %s" % sample.channel_data)


    @patch('serial.Serial', MockSerial)
    @patch('cloudbrain.connectors.openbci.OpenBCIConnector.start',
           mock_start)
    def test_OpenBCIConnector(self):
        board = OpenBCIConnector()
        callbacks = {self.metric_name: self.validate_start_method}
        board.start(callbacks)


    @patch('pika.BlockingConnection', MockBlockingConnection)
    def test_PikaPublisher(self):

        options = {"rabbitmq_address": self.rabbitmq_address,
                   "rabbitmq_user": self.rabbitmq_user,
                   "rabbitmq_pwd": self.rabbitmq_pwd}

        publisher = PikaPublisher(self.base_routing_key, **options)
        publisher.connect()
        publisher.register(self.metric_name, self.num_channels,
                           self.buffer_size)

        for metric_name in publisher.metrics_to_num_channels().keys():
            routing_key = "%s:%s" % (self.base_routing_key, metric_name)
            self.assertEqual(publisher.channels[routing_key].published_count, 0)
            self.assertEqual(publisher.channels[routing_key].message_published,
                             None)

            publisher.publish(metric_name, self.message)
            self.assertEqual(publisher.channels[routing_key].published_count, 0)

            publisher.publish(metric_name, self.message)
            self.assertEqual(publisher.channels[routing_key].published_count, 1)

            expected_message = [{
                "channel_5": 5, "channel_4": 4, "channel_7": 7,
                "channel_6": 6, "channel_1": 1, "channel_0": 0,
                "channel_3": 3, "channel_2": 2, "timestamp": 100
            }, {
                "channel_5": 5, "channel_4": 4, "channel_7": 7,
                "channel_6": 6, "channel_1": 1, "channel_0": 0,
                "channel_3": 3, "channel_2": 2, "timestamp": 100
            }]

            published_message = json.loads(
                publisher.channels[routing_key].message_published)
            self.assertEqual(published_message, expected_message)


    @patch('serial.Serial', MockSerial)
    @patch('pika.BlockingConnection', MockBlockingConnection)
    @patch('cloudbrain.connectors.openbci.OpenBCIConnector.start',
           mock_start)
    def test_OpenBCISource(self):
        options = {"rabbitmq_address": self.rabbitmq_address,
                   "rabbitmq_user": self.rabbitmq_user,
                   "rabbitmq_pwd": self.rabbitmq_pwd}

        publisher = PikaPublisher(self.base_routing_key, **options)
        publisher.connect()
        publisher.register(self.metric_name, self.num_channels,
                           self.buffer_size)

        publishers = [publisher]
        subscribers = []
        source = OpenBCISource(subscribers=subscribers,
                               publishers=publishers,
                               port=self.port,
                               baud=self.baud,
                               filter_data=self.filter_data)
        source.start()

        pub = source.publishers[0]

        routing_key = "%s:%s" % (self.base_routing_key, self.metric_name)
        self.assertEqual(pub.channels[routing_key].published_count, 0)
        self.assertEqual(pub.channels[routing_key].message_published,
                         None, "No messages should have been sent yet.")

@@ -2315,16 +2315,50 @@
 ock_pwd'
+%0A        self.rabbitmq_vhost = '/'
 %0A%0A      
@@ -3305,32 +3305,90 @@
 elf.rabbitmq_pwd
+,%0A                   %22rabbitmq_vhost%22: self.rabbitmq_vhost
 %7D%0A%0A        publi
@@ -5227,16 +5227,74 @@
 itmq_pwd
+,%0A                   %22rabbitmq_vhost%22: self.rabbitmq_vhost
 %7D%0A%0A     

Fix openbci tests