# Copyright 2017 Mycroft AI Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from os.path import dirname, join, abspath
import unittest
import unittest.mock as mock

import mycroft.audio.audioservice as audio_service
from mycroft.messagebus import Message

from .services.working import WorkingBackend
"""Tests for Audioservice class"""

seek_message = Message('seek', data={'seconds': 5})


class MockEmitter:
    def __init__(self):
        self.reset()

    def once(self, event_type, function):
        pass

    def on(self, event_type, function):
        pass

    def emit(self, message):
        self.types.append(message.msg_type)
        self.results.append(message.data)

    def get_types(self):
        return self.types

    def get_results(self):
        return self.results

    def remove(self, *args, **kwargs):
        pass

    def reset(self):
        self.types = []
        self.results = []


def setup_mock_backends(mock_load_services, emitter):
    backend = WorkingBackend({}, emitter)
    second_backend = WorkingBackend({}, emitter, 'second')
    mock_load_services.return_value = [backend, second_backend]
    return backend, second_backend


class TestService(unittest.TestCase):
    emitter = MockEmitter()
    service_path = abspath(join(dirname(__file__), 'services'))

    def test_load(self):
        service = audio_service.load_services({}, TestService.emitter,
                                              TestService.service_path)
        self.assertEqual(len(service), 1)

    @mock.patch('mycroft.audio.audioservice.load_services')
    def test_audio_backend_shutdown(self, mock_load_services):
        """Test shutdown of audio backend."""
        backend, second_backend = setup_mock_backends(mock_load_services,
                                                      self.emitter)
        service = audio_service.AudioService(self.emitter)
        service.load_services()

        service.default = backend

        # Check that all backend shutdown methods are called on audioservice
        # shutdown
        service.shutdown()
        self.assertTrue(backend.shutdown.called)
        self.assertTrue(second_backend.shutdown.called)

    @mock.patch('mycroft.audio.audioservice.load_services')
    def test_audio_service_track_start(self, mock_load_services):
        """Test start of new track messages."""
        backend, second_backend = setup_mock_backends(mock_load_services,
                                                      self.emitter)
        service = audio_service.AudioService(self.emitter)
        service.load_services()
        service.default = backend

        self.emitter.reset()
        service.track_start('The universe song')
        service.track_start(None)
        self.assertEqual(self.emitter.types, ['mycroft.audio.playing_track',
                                              'mycroft.audio.queue_end'])
        self.assertEqual(self.emitter.results,
                         [{'track': 'The universe song'}, {}])

        service.shutdown()

    @mock.patch('mycroft.audio.audioservice.load_services')
    def test_audio_service_methods_not_playing(self, mock_load_services):
        """Check that backend methods aren't called when stopped."""
        backend, second_backend = setup_mock_backends(mock_load_services,
                                                      self.emitter)
        mock_load_services.return_value = [backend, second_backend]

        service = audio_service.AudioService(self.emitter)
        service.load_services()

        service.default = backend

        # Check that next and prev aren't called if there is nothing playing
        service._next()
        self.assertFalse(backend.next.called)
        service._prev()
        self.assertFalse(backend.previous.called)
        service._pause()
        self.assertFalse(backend.pause.called)
        service._resume()
        self.assertFalse(backend.resume.called)
        service._seek_forward(seek_message)
        self.assertFalse(backend.seek_forward.called)
        service._seek_backward(seek_message)
        self.assertFalse(backend.seek_backward.called)
        service._lower_volume()
        self.assertFalse(service.volume_is_low)
        self.assertFalse(backend.lower_volume.called)
        service._restore_volume()
        self.assertFalse(backend.lower_volume.called)

        service.shutdown()

    @mock.patch('mycroft.audio.audioservice.load_services')
    def test_audio_service_methods_playing(self, mock_load_services):
        """Check that backend methods are called during playback."""
        backend, second_backend = setup_mock_backends(mock_load_services,
                                                      self.emitter)
        mock_load_services.return_value = [backend, second_backend]

        service = audio_service.AudioService(self.emitter)
        service.load_services()

        service.default = backend

        # Check that play doesn't play unsupported media uri type
        m = Message('audio.service.play', data={'tracks': ['asdf://hello']})
        service._play(m)
        self.assertFalse(backend.play.called)

        # Check that play plays supported media uri type
        m = Message('audio.service.play', data={'tracks': ['http://hello']})
        service._play(m)
        self.assertTrue(backend.play.called)

        # Check that next and prev are called if a backend is playing.
        service._next()
        self.assertTrue(backend.next.called)
        service._prev()
        self.assertTrue(backend.previous.called)
        service._pause()
        self.assertTrue(backend.pause.called)
        service._resume()
        self.assertTrue(backend.resume.called)
        service._lower_volume()
        self.assertTrue(service.volume_is_low)
        self.assertTrue(backend.lower_volume.called)
        service._restore_volume()
        self.assertFalse(service.volume_is_low)
        self.assertTrue(backend.lower_volume.called)

        # Check that play respects requested backends
        m = Message('audio.service.play',
                    data={'tracks': [['http://hello', 'audio/mp3']],
                          'utterance': 'using second'})
        service._play(m)
        self.assertTrue(second_backend.play.called)

        service._seek_forward(seek_message)
        second_backend.seek_forward.assert_called_with(5)
        service._seek_backward(seek_message)
        second_backend.seek_backward.assert_called_with(5)

        # Check that stop stops the active backend only if stop is received
        # more than 1 second from last play.
        second_backend.stop.reset_mock()
        self.assertFalse(second_backend.stop.called)
        service._stop()
        self.assertFalse(second_backend.stop.called)
        service.play_start_time -= 1
        service._stop()
        self.assertTrue(second_backend.stop.called)

        service.shutdown()

    @mock.patch('mycroft.audio.audioservice.load_services')
    def test_audio_service_queue_methods(self, mock_load_services):
        """Check that backend methods are called during playback."""
        backend, second_backend = setup_mock_backends(mock_load_services,
                                                      self.emitter)
        mock_load_services.return_value = [backend, second_backend]

        service = audio_service.AudioService(self.emitter)
        service.load_services()

        service.default = backend

        # Check that play doesn't play unsupported media uri type
        # Test queueing starts playback if stopped
        backend.play.reset_mock()
        backend.add_list.reset_mock()
        m = Message('audio.service.queue', data={'tracks': ['http://hello']})
        service._queue(m)
        backend.add_list.called_with(['http://hello'])
        self.assertTrue(backend.play.called)

        # Test queuing doesn't call play if play is in progress
        backend.play.reset_mock()
        backend.add_list.reset_mock()
        service._queue(m)
        backend.add_list.called_with(['http://hello'])
        self.assertFalse(backend.play.called)

        service.shutdown()


if __name__ == "__main__":
    unittest.main()
