#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>        // for close()
#include <arpa/inet.h>     // for sockaddr_in, inet_ntoa()
#include <netinet/in.h>    // for INADDR_ANY
#include <sys/types.h>     // <-- needed on macOS
#include <sys/socket.h>    // for socket functions

/*
    Receive TCP packets using C (single client only)

	Author: Tim Pierson, Dartmouth CS60, Fall 2026
            Assisted by Microsoft Copilot
	
	run: gcc receive_tcp.c -o receive
            ./receive
	
	send data from netcat in another terminal or computer (replace IP address)
	nc -t 127.0.0.1 9090
	then type messages
*/

#define HOST "127.0.0.1"
#define PORT 9090
#define BUFSIZE 1024

int main() {
    int server_fd, client_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    char buffer[BUFSIZE];

    // 1. Create TCP socket
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 2. Bind socket to IP and port
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr(HOST);

    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 3. Listen for connections
    if (listen(server_fd, 5) == -1) {
        perror("listen");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("Server listening on %s:%d...\n", HOST, PORT);

    // 4. Accept a client
    client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
    if (client_fd == -1) {
        perror("accept");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("Connected by %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

    // Get local IP/port for this client connection
    struct sockaddr_in local_addr;
    socklen_t local_len = sizeof(local_addr);
    if (getsockname(client_fd, (struct sockaddr *)&local_addr, &local_len) == 0) {
        printf("Using IP %s and port %d for this client\n",
               inet_ntoa(local_addr.sin_addr), ntohs(local_addr.sin_port));
    }

    // 5. Communication loop
    while (1) {
        ssize_t n = recv(client_fd, buffer, BUFSIZE - 1, 0);
        if (n <= 0) {
            break; // client disconnected or error
        }
        buffer[n] = '\0'; // null terminate
        printf("Received: %s\n", buffer);

        // Send back acknowledgment
        char response[BUFSIZE + 32];
        snprintf(response, sizeof(response), "Message received: %s", buffer);
        send(client_fd, response, strlen(response), 0);
    }

    // 6. Cleanup
    close(client_fd);
    close(server_fd);
    return 0;
}
