# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)

# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html

from hscommon.testutil import eq_
from hscommon.notify import Broadcaster, Listener, Repeater


class HelloListener(Listener):
    def __init__(self, broadcaster):
        Listener.__init__(self, broadcaster)
        self.hello_count = 0

    def hello(self):
        self.hello_count += 1


class HelloRepeater(Repeater):
    def __init__(self, broadcaster):
        Repeater.__init__(self, broadcaster)
        self.hello_count = 0

    def hello(self):
        self.hello_count += 1


def create_pair():
    b = Broadcaster()
    listener = HelloListener(b)
    return b, listener


def test_disconnect_during_notification():
    # When a listener disconnects another listener the other listener will not receive a
    # notification.
    # This whole complication scheme below is because the order of the notification is not
    # guaranteed. We could disconnect everything from self.broadcaster.listeners, but this
    # member is supposed to be private. Hence, the '.other' scheme
    class Disconnecter(Listener):
        def __init__(self, broadcaster):
            Listener.__init__(self, broadcaster)
            self.hello_count = 0

        def hello(self):
            self.hello_count += 1
            self.other.disconnect()

    broadcaster = Broadcaster()
    first = Disconnecter(broadcaster)
    second = Disconnecter(broadcaster)
    first.other, second.other = second, first
    first.connect()
    second.connect()
    broadcaster.notify("hello")
    # only one of them was notified
    eq_(first.hello_count + second.hello_count, 1)


def test_disconnect():
    # After a disconnect, the listener doesn't hear anything.
    b, listener = create_pair()
    listener.connect()
    listener.disconnect()
    b.notify("hello")
    eq_(listener.hello_count, 0)


def test_disconnect_when_not_connected():
    # When disconnecting an already disconnected listener, nothing happens.
    b, listener = create_pair()
    listener.disconnect()


def test_not_connected_on_init():
    # A listener is not initialized connected.
    b, listener = create_pair()
    b.notify("hello")
    eq_(listener.hello_count, 0)


def test_notify():
    # The listener listens to the broadcaster.
    b, listener = create_pair()
    listener.connect()
    b.notify("hello")
    eq_(listener.hello_count, 1)


def test_reconnect():
    # It's possible to reconnect a listener after disconnection.
    b, listener = create_pair()
    listener.connect()
    listener.disconnect()
    listener.connect()
    b.notify("hello")
    eq_(listener.hello_count, 1)


def test_repeater():
    b = Broadcaster()
    r = HelloRepeater(b)
    listener = HelloListener(r)
    r.connect()
    listener.connect()
    b.notify("hello")
    eq_(r.hello_count, 1)
    eq_(listener.hello_count, 1)


def test_repeater_with_repeated_notifications():
    # If REPEATED_NOTIFICATIONS is not empty, only notifs in this set are repeated (but they're
    # still dispatched locally).
    class MyRepeater(HelloRepeater):
        REPEATED_NOTIFICATIONS = {"hello"}

        def __init__(self, broadcaster):
            HelloRepeater.__init__(self, broadcaster)
            self.foo_count = 0

        def foo(self):
            self.foo_count += 1

    b = Broadcaster()
    r = MyRepeater(b)
    listener = HelloListener(r)
    r.connect()
    listener.connect()
    b.notify("hello")
    b.notify("foo")  # if the repeater repeated this notif, we'd get a crash on HelloListener
    eq_(r.hello_count, 1)
    eq_(listener.hello_count, 1)
    eq_(r.foo_count, 1)


def test_repeater_doesnt_try_to_dispatch_to_self_if_it_cant():
    # if a repeater doesn't handle a particular message, it doesn't crash and simply repeats it.
    b = Broadcaster()
    r = Repeater(b)  # doesnt handle hello
    listener = HelloListener(r)
    r.connect()
    listener.connect()
    b.notify("hello")  # no crash
    eq_(listener.hello_count, 1)


def test_bind_messages():
    b, listener = create_pair()
    listener.bind_messages({"foo", "bar"}, listener.hello)
    listener.connect()
    b.notify("foo")
    b.notify("bar")
    b.notify("hello")  # Normal dispatching still work
    eq_(listener.hello_count, 3)
