'''
Docstring for pyServer

The following protocol is in use:

Handshake:
Client -> Server: "Gday to you"
Server -> Client: "mkay"

Data Exchange:
Client -> Server: MyWish
Server -> Client: YourStuff

Termination:
Client -> Server: Done
Server -> Client: GoAway

Error:
Server -> Client: Not expected

This server uses proper message framing with newline ('\\n') as a
delimiter. This makes the server robust against TCP's stream nature,
where recv() may split a single send() across multiple recv() calls
or coalesce multiple sends into one recv().

Framing rule:
    Every message ends with a single newline ('\\n'). The newline is
    *not* part of the application-level message -- it only marks where
    one message ends and the next begins. Messages must therefore not
    contain newlines themselves.
'''

import socket

HOST = "127.0.0.1"
PORT = 1337


def recv_message(sock, buffer):
    """Return (message, new_buffer). message is None if the peer closed.

    TCP is a byte stream, so a single recv() may give us only part of a
    message or several messages concatenated. We therefore keep reading
    until the buffer contains a '\\n', then split off exactly one message
    and keep the rest in the buffer for the next call.
    """
    while b"\n" not in buffer:
        chunk = sock.recv(4096)
        if not chunk:
            # Peer closed. Anything still in the buffer is an incomplete
            # message and is discarded -- the protocol requires every
            # message to be newline-terminated.
            return None, buffer
        buffer += chunk
    line, buffer = buffer.split(b"\n", 1)
    return line.decode("utf-8"), buffer


def send_message(sock, msg):
    """Send one message, automatically appending the framing newline."""
    sock.sendall(msg.encode("utf-8") + b"\n")


def handle_client(conn):
    """Run one full session (handshake -> data exchange -> termination)."""
    buffer = b""
    established = False
    ready_to_receive = False

    try:
        while True:
            msg, buffer = recv_message(conn, buffer)

            if msg is None:  # client disconnected
                break

            if msg == "Done":
                send_message(conn, "GoAway")
                break

            if not established:
                if msg == "Gday to you":
                    send_message(conn, "mkay")
                    established = True
                else:
                    send_message(conn, "Not expected")
                    break

            elif not ready_to_receive:
                if msg == "MyWish":
                    send_message(conn, "YourStuff")
                    ready_to_receive = True
                else:
                    send_message(conn, "Not expected")
                    break

            else:
                send_message(conn, f"You sent: {msg}")
    finally:
        conn.close()


if __name__ == '__main__':
    serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serversocket.bind((HOST, PORT))
    serversocket.listen(5)

    try:
        # Accept one client at a time. Each iteration handles a full session
        # (handshake -> data exchange -> termination) before returning to
        # wait for the next client.
        while True:
            (clientsocket, address) = serversocket.accept()
            handle_client(clientsocket)
    except KeyboardInterrupt:
        pass
    finally:
        serversocket.close()
