/*
 * Process Ping Pong
 * Martin Klöckner - mklockner@fi.uba.ar
 *
 * Based on examples given in wait(2) man page
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>

#define LOG_ENABLE     true
#define PIPE_READ_END  0
#define PIPE_WRITE_END 1
#define DELAY_MS       100

#if true == LOG_ENABLE
#define LOG(fmt, ...) do {                  \
        printf("[LOG] "fmt"\n", ##__VA_ARGS__); \
    } while (0)
#define LOG_ID(fmt, ...) do {                                \
        printf("[LOG][%ld] "fmt"\n", proc_count, ##__VA_ARGS__); \
    } while (0)
#else
#define LOG(fmt, ...)
#define PROC_LOG(fmt, ...)
#endif

void sleep_ms(long ms)
{
    struct timespec ts = {
        .tv_sec = ms / 1000,
        .tv_nsec = (ms % 1000) * 1000000L
    };

    nanosleep(&ts, NULL);
}

/*
 * Parent ===pipefd_1===> Child
 * Parent <==pipefd_2==== Child
 */

int main (void)
{
    int wstatus, cpid, proc_count;
    int pipefd_1[2], pipefd_2[2];
    uint32_t ball;

    proc_count = 0;
    ball = 0;

    if ((pipe(pipefd_1) == -1) || (pipe(pipefd_2) == -1))
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    cpid = fork();

    if (cpid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0)
    {
        // Child
        proc_count++;
        LOG_ID("PID is %jd", (intmax_t) getpid());
        sleep_ms(DELAY_MS);

        if (close(pipefd_1[PIPE_WRITE_END]) == -1)
        {
            // Close unused pipefd_1 write end
            perror("close");
            exit(EXIT_FAILURE);
        }

        if (close(pipefd_2[PIPE_READ_END]) == -1)
        {
            // Close unused pipefd_2 read end
            perror("close");
            exit(EXIT_FAILURE);
        }

        while (true)
        {
            // Wait for response from the other end
            while (read(pipefd_1[PIPE_READ_END], &ball, sizeof(ball)) <= 0)
            {
                ;
            }
            sleep_ms(DELAY_MS);

            ball++;
            LOG_ID("%d", ball);
            write(pipefd_2[PIPE_WRITE_END], &ball, sizeof(ball));
        }

        exit(EXIT_SUCCESS);
    }
    else
    {
        // Parent
        LOG_ID("PID is %jd", (intmax_t) getpid());
        sleep_ms(DELAY_MS);

        if (close(pipefd_1[PIPE_READ_END]) == -1)
        {
            // Close unused pipefd_1 read end
            perror("close");
            exit(EXIT_FAILURE);
        }

        if (close(pipefd_2[PIPE_WRITE_END]) == -1)
        {
            // Close unused pipefd_2 write end
            perror("close");
            exit(EXIT_FAILURE);
        }

        while (true)
        {
            ball++;
            LOG_ID("%d", ball);
            write(pipefd_1[PIPE_WRITE_END], &ball, sizeof(ball));

            // Wait for response from the other end
            while (read(pipefd_2[PIPE_READ_END], &ball, sizeof(ball)) <= 0)
            {
                ;
            }

            sleep_ms(DELAY_MS);
        }

        exit(EXIT_SUCCESS);
    }

    exit(EXIT_SUCCESS);
}
