from omegaconf import OmegaConf

import pymasep as pm
from pymasep.communication import Message
from test.unit.mocks.app_mock import AppMock


class BaseThreadedCommunicationMock:

    def __init__(self,
                 ident: str):
        self.id = ident
        # https://stackoverflow.com/questions/13610654/how-to-make-built-in-containers-sets-dicts-lists-thread-safe
        self._current_connections = dict()  # dict (id -> BaseCommunication)

    def add_connection(self, ident: str, rh) -> None:
        self._current_connections[ident] = rh

    def get_connection(self, ident):
        return self._current_connections[ident]

    def send_frame_to(self, ident: str, frame):
        self._current_connections[ident].put(frame.message)


def test_send_message_by_queue():
    app = AppMock()
    cfg = OmegaConf.create()
    sapp = pm.application.SubApp(app=app, received_q_id=0, cfg=cfg)
    sapp.register_connected_sub_app(sub_app_id=sapp.id,
                                    role=pm.application.ConnectedSubAppInfo.ROLE_NONE,
                                    sent_q=app.queues[0])
    msg = Message(Message.MESSAGE_TYPE_QUIT_GAME, None, sapp.id)

    sapp.send_message(msg, sapp.id)

    sapp.wait_message([Message.MESSAGE_TYPE_QUIT_GAME])
    assert msg == sapp.current_event
    assert app.queues[0].qsize() == 0


def test_send_message_by_socket():
    app = AppMock()
    cfg = OmegaConf.create()
    sapp = pm.application.SubApp(app=app, received_q_id=0, cfg=cfg)
    sapp.register_connected_sub_app(sub_app_id=sapp.id,
                                    role=pm.application.ConnectedSubAppInfo.ROLE_NONE,
                                    sent_q=None)
    # manual mocking
    sapp.remote_communication = BaseThreadedCommunicationMock(sapp.id)
    sapp.remote_communication.add_connection(sapp.id, app.queues[0])
    msg = Message(Message.MESSAGE_TYPE_QUIT_GAME, None, sapp.id)

    sapp.send_message(msg, sapp.id)

    sapp.wait_message([Message.MESSAGE_TYPE_QUIT_GAME])
    assert msg == sapp.current_event
    assert app.queues[0].qsize() == 0


def test_wait_message_blocking_msg_received():
    app = AppMock()
    cfg = OmegaConf.create()
    sapp = pm.application.SubApp(app=app, received_q_id=0, cfg=cfg)
    msg = Message(Message.MESSAGE_TYPE_QUIT_GAME, None, sapp.id)
    sapp.send_message_to_queue(msg, app.queues[0])

    sapp.wait_message([Message.MESSAGE_TYPE_QUIT_GAME])

    assert msg == sapp.current_event
    assert app.queues[0].qsize() == 0


def test_wait_message_non_blocking_no_msg():
    app = AppMock()
    cfg = OmegaConf.create()
    sapp = pm.application.SubApp(app=app, received_q_id=0, cfg=cfg)

    sapp.wait_message([Message.MESSAGE_TYPE_QUIT_GAME], False)

    assert sapp.current_event is None
    assert app.queues[0].qsize() == 0


def test_wait_message_non_blocking_msg_received():
    app = AppMock()
    cfg = OmegaConf.create()
    sapp = pm.application.SubApp(app=app, received_q_id=0, cfg=cfg)
    msg = Message(Message.MESSAGE_TYPE_QUIT_GAME, None, sapp.id)
    sapp.send_message_to_queue(msg, app.queues[0])

    sapp.wait_message([Message.MESSAGE_TYPE_QUIT_GAME], False)

    assert msg == sapp.current_event
    assert app.queues[0].qsize() == 0


def test_wait_message_non_blocking_unwanted_msg():
    app = AppMock()
    cfg = OmegaConf.create()
    sapp = pm.application.SubApp(app=app, received_q_id=0, cfg=cfg)
    msg = Message(Message.MESSAGE_TYPE_QUIT_GAME, None, sapp.id)
    sapp.send_message_to_queue(msg, app.queues[0])

    sapp.wait_message([Message.MESSAGE_TYPE_ACTION], False)

    assert sapp.current_event is None
    assert app.queues[0].qsize() == 0


def test_get_connected_id_from_agent_fname():
    app = AppMock()
    cfg = OmegaConf.create()
    sapp = pm.application.SubApp(app=app, received_q_id=0, cfg=cfg)
    sapp.register_connected_sub_app('interface_id_1', pm.application.ConnectedSubAppInfo.ROLE_ACTOR, None)
    sapp.connected_sub_app['interface_id_1'].agent_fname = 'agent1'
    sapp.register_connected_sub_app('interface_id_2', pm.application.ConnectedSubAppInfo.ROLE_ACTOR, None)
    sapp.connected_sub_app['interface_id_2'].agent_fname = 'agent2'
    sapp.register_connected_sub_app('interface_id_3', pm.application.ConnectedSubAppInfo.ROLE_OBSERVER, None)

    result = sapp.get_connected_id_from_agent_fname('agent1')
    result_not_interface_agent = sapp.get_connected_id_from_agent_fname('agent3')

    assert result == 'interface_id_1'
    assert result_not_interface_agent is None
