class RelayInterface:
    """
    Component that is responsible for every interaction with the Mesh Relay.
    It achieves this through the use of websockets. This component runs
    concurrently to the main program, similiar to Listener or Sender.
    """

    def __init__(self, crypto, relay_address, uid):
        self.crypto = crypto
        self.relay_address = relay_address
        self.uid = uid

    def start(self, parser):
        """Start the relay sender and Listener"""
        self.parser = parser
        self.sender = RelaySender()
        self.launch_listener_thread()

    def send(self, uid, msg):
        """Send a message on the mesh relay"""
        return self.sender.send(uid, msg)

    def connect_to_relay(self):
        """This runs the connection protocol"""
        self.relay = asyncio.get_event_loop().run_until_complete(self.get_mesh_relay(self.relay_address, self.uid))
        self.sender.relay_socket = self.relay

        # Send a subscription
        self.sender.send()

    def launch_listener_thread(self):
        """Daemonizes and launches the concurrent listener"""
        # self.serve_forever()
        listener_thread = threading.Thread(target=self.serve_forever)
        listener_thread.daemon = True
        listener_thread.start()

    def serve_forever(self):
        """Serves the Receiver loop until the program completes"""
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        self.connect_to_relay()
        asyncio.get_event_loop().run_until_complete(self.receive())

    @asyncio.coroutine
    def get_mesh_relay(self, relay_address, uid):
        """Connect to the mesh relay and return the active connection"""
        result = yield from websockets.connect("ws://{0}?uid={1}".format(relay_address, uid))
        return result

    @asyncio.coroutine
    def receive(self):
        """Run a receive loop forever"""
        while True:
            try:
                # Wait until we get data from the relay server
                recved = yield from self.relay.recv()
                relay_msg = json.loads(recved)
                self.handle_message(relay_msg)

            except websockets.exceptions.ConnectionClosed:
                """In the event of a relay closing the connection erroneously
                (rails issue?), reconnect to the relay immediately."""
                self.connect_to_relay()
                continue

    def handle_message(self, relay_msg):
        """Check validity of payload, then decrypt if we can"""
        # Validate that the message is appropriate
        if "message" in relay_msg and "type" not in relay_msg and "message" in relay_msg["message"]:
            try:
                to_decrypt = json.dumps(relay_msg["message"]).encode()
                decrypted = self.crypto.decrypt(to_decrypt)
                self.parser.handle_message(decrypted)
            except Exception as msg:
                Display.warn("Warning: General decryption failure\n\n{0}".format(msg))
 def start(self, parser):
     """Start the relay sender and Listener"""
     self.parser = parser
     self.sender = RelaySender()
     self.launch_listener_thread()