"""Serve the python console using socket communication."""

import asyncio
import socket
from functools import partial

from . import code


@asyncio.coroutine
def handle_connect(reader, writer, factory, banner=None):
    streams = reader, writer
    interface = factory(streams=streams)
    yield from interface.interact(banner=banner, stop=False,
                                  handle_sigint=False)
    writer.close()


@asyncio.coroutine
def start_interactive_server(factory=code.AsynchronousConsole,
                             host=None, port=None, path=None,
                             banner=None, *, loop=None):
    if (port is None) == (path is None):
        raise ValueError("Either a TCP port or a UDS path should be provided")
    if port is not None:
        # Override asyncio behavior (i.e serve on all interfaces by default)
        host = host or "localhost"
        start_server = partial(asyncio.start_server, host=host, port=port)
    else:
        start_server = partial(asyncio.start_unix_server, path=path)

    client_connected = partial(handle_connect, factory=factory, banner=banner)
    server = yield from start_server(client_connected, loop=loop)
    return server


@asyncio.coroutine
def start_console_server(host=None, port=None, path=None,
                         locals=None, filename="<console>", banner=None,
                         prompt_control=None, *, loop=None):
    factory = partial(
        code.AsynchronousConsole,
        locals=locals, filename=filename, prompt_control=prompt_control)
    server = yield from start_interactive_server(
        factory,
        host=host,
        port=port,
        path=path,
        banner=banner,
        loop=loop)
    return server


def print_server(server, name='console', file=None):
    interface = server.sockets[0].getsockname()
    if server.sockets[0].family != socket.AF_UNIX:
        interface = '{}:{}'.format(*interface)
    print('The {} is being served on {}'.format(name, interface), file=file)


def run(host=None, port=None, path=None):
    loop = asyncio.get_event_loop()
    coro = start_interactive_server(host=host, port=port, path=path)
    loop.server = loop.run_until_complete(coro)
    print_server(loop.server)
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass


def parse_server(server, parser=None):
    try:
        host, port = server.rsplit(':', maxsplit=1)
    except ValueError:
        host, port = 'localhost', server
    try:
        port = int(port)
    except (ValueError, TypeError):
        msg = "{!r} is not a valid server [HOST:]PORT".format(server)
        if not parser:
            raise ValueError(msg)
        parser.error(msg)
    return host, port
