"""
Negotiate About Window Size, *NAWS*.

rfc-1073_.
"""

# std imports
import sys
import struct
import asyncio
import platform

# 3rd party
import pytest
import pexpect

# local
import telnetlib3
from telnetlib3.telopt import SB, SE, IAC, NAWS, WILL
from telnetlib3.tests.accessories import create_server, open_connection, asyncio_connection


async def test_telnet_server_on_naws(bind_host, unused_tcp_port):
    """Test Server's Negotiate about window size (NAWS)."""
    _waiter = asyncio.Future()
    given_cols, given_rows = 40, 20

    class ServerTestNaws(telnetlib3.TelnetServer):
        def on_naws(self, rows, cols):
            super().on_naws(rows, cols)
            _waiter.set_result(self)

    async with create_server(
        protocol_factory=ServerTestNaws, host=bind_host, port=unused_tcp_port, connect_maxwait=0.5
    ):
        async with asyncio_connection(bind_host, unused_tcp_port) as (reader, writer):
            writer.write(IAC + WILL + NAWS)
            writer.write(IAC + SB + NAWS + struct.pack("!HH", given_cols, given_rows) + IAC + SE)

            srv_instance = await asyncio.wait_for(_waiter, 0.5)
            assert srv_instance.get_extra_info("cols") == given_cols
            assert srv_instance.get_extra_info("rows") == given_rows


async def test_telnet_client_send_naws(bind_host, unused_tcp_port):
    """Test Client's NAWS of callback method send_naws()."""
    _waiter = asyncio.Future()
    given_cols, given_rows = 40, 20

    class ServerTestNaws(telnetlib3.TelnetServer):
        def on_naws(self, rows, cols):
            super().on_naws(rows, cols)
            _waiter.set_result((rows, cols))

    async with create_server(
        protocol_factory=ServerTestNaws, host=bind_host, port=unused_tcp_port, connect_maxwait=0.5
    ):
        async with open_connection(
            host=bind_host, port=unused_tcp_port, cols=given_cols, rows=given_rows
        ) as (reader, writer):
            recv_rows, recv_cols = await asyncio.wait_for(_waiter, 0.5)
            assert recv_cols == given_cols
            assert recv_rows == given_rows


@pytest.mark.skipif(sys.platform == "win32", reason="pexpect.spawn requires Unix PTY")
@pytest.mark.skipif(
    tuple(map(int, platform.python_version_tuple()[:2])) > (3, 10),
    reason="those shabby pexpect maintainers still use @asyncio.coroutine",
)
async def test_telnet_client_send_tty_naws(bind_host, unused_tcp_port):
    """Test Client's NAWS of callback method send_naws()."""
    _waiter = asyncio.Future()
    given_cols, given_rows = 40, 20
    prog, args = "telnetlib3-client", [
        bind_host,
        str(unused_tcp_port),
        "--loglevel=warning",
        "--connect-maxwait=0.010",
    ]

    class ServerTestNaws(telnetlib3.TelnetServer):
        def on_naws(self, rows, cols):
            super().on_naws(rows, cols)
            _waiter.set_result((cols, rows))
            asyncio.get_event_loop().call_soon(self.connection_lost, None)

    async with create_server(
        protocol_factory=ServerTestNaws, host=bind_host, port=unused_tcp_port, connect_maxwait=0.5
    ):
        proc = pexpect.spawn(prog, args, dimensions=(given_rows, given_cols))
        await proc.expect(pexpect.EOF, async_=True, timeout=5)
        assert proc.match == pexpect.EOF

        recv_cols, recv_rows = await asyncio.wait_for(_waiter, 0.5)
        assert recv_cols == given_cols
        assert recv_rows == given_rows


async def test_telnet_client_send_naws_65534(bind_host, unused_tcp_port):
    """Test Client's NAWS boundary values."""
    _waiter = asyncio.Future()
    given_cols, given_rows = 9999999, -999999
    expect_cols, expect_rows = 65535, 0

    class ServerTestNaws(telnetlib3.TelnetServer):
        def on_naws(self, rows, cols):
            super().on_naws(rows, cols)
            _waiter.set_result((cols, rows))

    async with create_server(
        protocol_factory=ServerTestNaws, host=bind_host, port=unused_tcp_port, connect_maxwait=0.5
    ):
        async with open_connection(
            host=bind_host, port=unused_tcp_port, cols=given_cols, rows=given_rows
        ) as (reader, writer):
            recv_cols, recv_rows = await asyncio.wait_for(_waiter, 0.5)
            assert recv_cols == expect_cols
            assert recv_rows == expect_rows


async def test_naws_without_will(bind_host, unused_tcp_port):
    """NAWS subnegotiation received without prior WILL NAWS is tolerated."""
    _waiter = asyncio.Future()
    given_cols, given_rows = 132, 43

    class ServerTestNaws(telnetlib3.TelnetServer):
        def on_naws(self, rows, cols):
            super().on_naws(rows, cols)
            _waiter.set_result(self)

    async with create_server(
        protocol_factory=ServerTestNaws, host=bind_host, port=unused_tcp_port, connect_maxwait=0.5
    ):
        async with asyncio_connection(bind_host, unused_tcp_port) as (reader, writer):
            writer.write(IAC + SB + NAWS + struct.pack("!HH", given_cols, given_rows) + IAC + SE)

            srv_instance = await asyncio.wait_for(_waiter, 0.5)
            assert srv_instance.get_extra_info("cols") == given_cols
            assert srv_instance.get_extra_info("rows") == given_rows
