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

#include "logger.h"

#define STRING_LENGTH (512)

static void
gup( log_handler * logs, int pipefd[2], int cpid);

static void
gup_handler( char c, int kids, int pipefd[2], log_handler * logs )
{
	char buf[STRING_LENGTH+1], str[256];
	
	pid_t cpid;

	switch( c )
	{
		case 'q':
			(void) sprintf(str, "Child %d normal exit\n", kids);
			write_to_all_logs(logs, str);
			close_all_logs(logs);
			exit(EXIT_FAILURE);
		break;

		case 'f':
			kids++;

			// When you fork, be sure to print the process id of the child
			// (if the fork succeeds) and an error message otherwise
			
			cpid = fork();
			
			if( cpid == -1 )
			{
				perror("fork");
				exit(EXIT_FAILURE);
			}

			if( cpid == 0 )
			{
				// Child reads from pipe
				gup(logs, pipefd, kids);

				close_all_logs(logs);
				_exit(EXIT_SUCCESS);
			}
			else
			{
				// Parent writes buffer to child 
				if( fgets(buf, STRING_LENGTH, stdin) )
					if( !write(pipefd[1], buf, strlen(buf)) )
						perror("pipe write");

				// Close unused read end
				close(pipefd[0]);
				close(pipefd[1]);
				
				wait(NULL);

				close_all_logs(logs);
				exit(EXIT_SUCCESS);
			}
	}
}

static void
gup(log_handler * logs, int pipefd[2], int cpid)
{

    // Put any required declarations and initialisations here
	if( !logs || !pipefd )
		return;

	// Close unused write end
    close(pipefd[1]);

    // just for sure - open one more log file ...
    // log3 is line buffered - a line of output is written 
    // immediately on encountering an end-of-line
	setvbuf(open_log("child.log3", "wt+", logs), NULL, _IOLBF, BUFSIZ);

    // Report your existence
	char * str = malloc(256 * sizeof(char *));
	
	strcpy(str, "This is child number %d\n");
	(void) sprintf(str, str, cpid);
	write_to_all_logs(logs, str); 
	
	char * str_read = "Child %d read character %c '%x'\n";

    char c;

	while( read(pipefd[0], &c, 1) > 0 )
    {
		// Read 1 character from the pipe into 'c' here.
		// Be sure to test that you have indeed read a character.
		// If you get an error (read returns a negative value) or
		// an end-of-file (read returns zero), end your program
		// with an exit(1)
		
		(void) sprintf(str, str_read, cpid, c, c);
		write_to_all_logs(logs, str); 
		
		// process the character here
		gup_handler(c, cpid, pipefd, logs);
    }
}

static void
process_handler( log_handler * logs )
{
    int pipefd[2];
    
	// create the pipe somewhere around here (why?).
    // remember to test for success!! 
	if( pipe(pipefd) == -1 )
	{
		perror("pipe");
		exit(EXIT_FAILURE);
	}
	
	char * str = malloc(256 * sizeof(char)),
	     * str_read = "Parent %d read character %c '%x'\n";

	strcpy(str, "This is the parent process %d\n");
	(void) sprintf(str, str, 0);
	write_to_all_logs(logs, str); 

	char c; 
    
	int kids = 0;

    while( (c = getchar()) )
    {
		// Anyway, read a character, echo it to both log files 
		// and write it into the pipe (unless you have to call fork)

		(void) sprintf(str, str_read, 0, c, c);
		write_to_all_logs(logs, str); 
		
		gup_handler(c, kids, pipefd, logs);
    }
}

int
main( void )
{
    // log1 is buffered; log2 is not buffered
	// which means that output to log2 is 
	// written immediately. 
	
	log_handler * logs = create_logger();
	(void) open_log("child.log1", "wt+", logs);
    setvbuf(open_log("child.log2", "wt+", logs), NULL, _IONBF, BUFSIZ);
	
	process_handler(logs);

    // close all open log files
	close_all_logs(logs);

    return EXIT_SUCCESS;
}
