/*
 * Student name .... Sander van Veen & Taddeus Kroes
 * Student email ... sandervv@gmail.com & taddeuskroes@hotmail.com
 * Collegekaart .... 6167969 & 6054129
 * Date ............ 07.11.2010
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

static pid_t parent_pid;

/*
 * Read from pipe (child processes only).
 */
static void gup(FILE * log1, FILE * log2, int pipe_id[2], int myNumber)
{
    FILE * log3;
    char c;
    int antidote = 0;
    /* Put any required declarations and initialisations here */

    close(pipe_id[1]);

    /* Report your existence */
    fprintf(log1, "This is child number %d\n", myNumber);
    fprintf(log2, "This is child number %d\n", myNumber);

    /* Open a line buffered log. */
    log3 = fopen("child.log3", "wt+");
    setvbuf(log3, NULL, _IOLBF, BUFSIZ);

    fprintf(log3, "This is child number %d\n", myNumber);

    do
    {
        /* Read next character from pipe */
        if( read(pipe_id[0], &c, 1) <= 0 )
        {
            /* Error while reading from the pipe, exit */
            fprintf(log1, "Child %d read from pipe with error "
                    "and exited\n", myNumber);
            fprintf(log2, "Child %d read from pipe with error "
                    "and exited\n", myNumber);
            fprintf(log3, "Child %d read from pipe with error "
                    "and exited\n", myNumber);
            fclose(log1);
            fclose(log2);
            fclose(log3);
            exit(1);
        }

        /* Character read successfully, process it */
        fprintf(log1, "Child %d read character %x: '%c'\n", myNumber, c, c);
        fprintf(log2, "Child %d read character %x: '%c'\n", myNumber, c, c);
        fprintf(log3, "Child %d read character %x: '%c'\n", myNumber, c, c);

        if( c == 'P' )
        {
            /* Decrease antidote level or exit the process */
            if( !antidote )
            {
                fprintf(log1, "Child %d has no antidote for poison "
                        "and will exit\n", myNumber);
                fprintf(log2, "Child %d has no antidote for poison "
                        "and will exit\n", myNumber);
                fprintf(log3, "Child %d has no antidote for poison "
                        "and will exit\n", myNumber);
                break;
            }
            else
            {
                antidote--;
                fprintf(log1, "antidote used (%d left)\n", antidote);
                fprintf(log2, "antidote used (%d left)\n", antidote);
                fprintf(log3, "antidote used (%d left)\n", antidote);
            }
        }
        else if( c == 'A' )
        {
            /* Increase antidote level */
            antidote++;
            fprintf(log1, "Child %d finds antidote (now has %d)\n",
                    myNumber, antidote);
            fprintf(log2, "Child %d finds antidote (now has %d)\n",
                    myNumber, antidote);
            fprintf(log3, "Child %d finds antidote (now has %d)\n",
                    myNumber, antidote);
        }
    } while( c );

    /* Close logs and exit normally */
    fprintf(log1, "Child %d normal exit\n", myNumber);
    fprintf(log2, "Child %d normal exit\n", myNumber);
    fprintf(log3, "Child %d normal exit\n", myNumber);
    fclose(log1);
    fclose(log2);
    fclose(log3);
    exit(0);
}

/*
 * Signal handler for SIGINT, SIGTERM and SIGCHLD.
 */
    void
signal_handler(int signum)
{
    char * sig = "TERM";
    pid_t pid;

    switch( signum )
    {
        case SIGINT:
            sig = "INT";
        case SIGTERM:
            if( (pid = getpid()) == parent_pid )
            {
                printf("Parent process received SIG%s and will exit\n", sig);
            }
            else
            {
                printf("Process with id %d received SIG%s and will exit\n",
                        pid, sig);
            }
            exit(1);
        case SIGCHLD:
            puts("Parent process received SIGCHLD");
            pid = wait(NULL);
            printf("Child with id %d exited\n", pid);
            break;
        default:
            puts("This handler is unapplicable for this type of signal");
    }
}

/*
 * Main funtion, initiates the program.
 */
    int
main(void)
{
    FILE * log1;
    FILE * log2;
    char c;
    int kiddoCount = 0, pipe_id[2];
    struct sigaction action = {.sa_handler = signal_handler};

    /* Save parent process id for signal handler */
    parent_pid = getpid();

    /* Create pipe, exit with error on failure */
    if( pipe(pipe_id) )
    {
        fprintf(stderr, "Error %d occured while creating the pipe\n",
                errno);
        exit(1);
    }

    /* Bind signal handlers */
    if( sigaction(SIGINT, &action, NULL)
            || sigaction(SIGTERM, &action, NULL)
            || sigaction(SIGCHLD, &action, NULL) )
    {
        perror("An error occured while binding the signal handlers\n");
        exit(1);
    }

    /* Open one buffered and one unbuffered log */
    log1 = fopen("child.log1", "wt+");
    log2 = fopen("child.log2", "wt+");
    setvbuf(log2, NULL, _IONBF, BUFSIZ);

    /* Keep reading input */
    while( (c = getchar()) )
    {
        if( c == EOF )
        {
            if( errno == EINTR )
            {
                /* Wait for signal handler to end */
                continue;
            }
            else
            {
                puts("Error while reading input from stdin");
                fclose(log1);
                fclose(log2);
                exit(1);
            }
        }

        /* Ignore newlines and spaces */
        if( c == '\n' || c == 32 )
        {
            continue;
        }

        fprintf(log1, "Parent read character %x: '%c'\n", c, c);
        fprintf(log2, "Parent read character %x: '%c'\n", c, c);
        printf("Character '%c' read\n", c);

        if( c == 'q' )
        {
            /* Exit program */
            break;
        }
        else if( c != 'f' )
        {
            /* Write character to pipe for child processes */
            if( write(pipe_id[1], &c, 1) <= 0 )
            {
                perror("Cannot write to pipe\n");
                exit(1);
            }

            /* Wait for child process to 'eat the poison' */
            //if( c == 'P' )
            //	wait(NULL);
        }
        else
        {
            pid_t child_id;

            kiddoCount++;

            /* When you fork, be sure to print the process id of the child
               (if the fork succeeds) and an error message otherwise */

            switch( (child_id = fork()) )
            {
                case -1:
                    /* An error occured while forking */
                    perror("Child could not be created\n");
                    exit(1);
                case 0:
                    /* Child process after fork: Start reading from pipe */
                    gup(log1, log2, pipe_id, kiddoCount);
                    break;
                default:
                    /* Parent proces after fork: Print child's process id */
                    fprintf(log1, "Started child process %d with id %d\n",
                            kiddoCount, child_id);
                    fprintf(log2, "Started child process %d with id %d\n",
                            kiddoCount, child_id);
                    printf("Started child process %d with id %d\n",
                            kiddoCount, child_id);
            }
        }
    }

    /* Close logs and exit normally */
    fprintf(log1, "Program exited normally\n");
    fprintf(log2, "Program exited normally\n");
    fclose(log1);
    fclose(log2);

    return 0;
}
