#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

int main() {
    pid_t rf;           // PID del hijo
    int flag, status;   // para wait
    int resultado;      // para control de errores
    float numeroAleatorio; // número aleatorio generado
    float suma = 0.0;   // suma de los dos números
    int fileDes[2];     // array de 2 numero enteros, el primero es un descriptor de lectura y el derecho de escritura
    int i = 0;          // iterador

    // Crear la tubería
    resultado = pipe(fileDes);//pipe crea la tuberia con los dos descriptores, si devuelve -1 se ha producido un error
    if (resultado == -1) {
        printf("\nERROR al crear la tubería.\n");
        exit(1);
    }

    //esto es SO 
    // Crear el proceso hijo
    rf = fork();
    switch (rf) {
    case -1:
        printf("No se ha podido crear el proceso hijo...\n");
        exit(EXIT_FAILURE);

    case 0: // HIJO: lee la suma de la tubería y la imprime
        printf("[HIJO]: Mi PID es %d y mi PPID es %d\n", getpid(), getppid());
        close(fileDes[1]); // el hijo no escribe, cierra el extremo de escritura

        // Leer la suma del padre
        resultado = read(fileDes[0], &suma, sizeof(float));//sizeof(float) pq solo queremos leer un flotante
        //read es una función del include unistd
        //le pasas el descriptor de donde leer, la direccion de donde se guarda lo leido, y cuantos bytes leer
        if (resultado != sizeof(float)) {
            printf("\n[HIJO]: ERROR al leer de la tubería...\n");
            exit(EXIT_FAILURE);
        }
        printf("[HIJO]: La suma que me envió el padre es %f\n", suma);

        close(fileDes[0]); // cerrar extremo de lectura
        exit(EXIT_SUCCESS);
        break;

    default: // PADRE: genera dos números, los suma y los envía al hijo
        printf("[PADRE]: Mi PID es %d y el PID de mi hijo es %d\n", getpid(), rf);
        close(fileDes[0]); // el padre no lee, cierra el extremo de lectura

        srand(time(NULL)); // semilla para números aleatorios
        for (i = 0; i < 2; i++) {
            numeroAleatorio = rand() % 10; // número aleatorio entre 0 y 9
            suma += numeroAleatorio;
            printf("[PADRE]: Generando número aleatorio %f\n", numeroAleatorio);
        }

        // Enviar la suma al hijo por la tubería
        printf("[PADRE]: La suma es %f\n", suma);
        resultado = write(fileDes[1], &suma, sizeof(float));
        //write es para escribir, por eso ponemos el descriptor de escribir, al reves que antes, el resto es igual
        if (resultado != sizeof(float)) {
            printf("\n[PADRE]: ERROR al escribir en la tubería...\n");
            exit(EXIT_FAILURE);
        }

        close(fileDes[1]); // cerrar extremo de escritura
        printf("[PADRE]: Tubería cerrada...\n");


        // Es SO, no hace falta entenderlo del todo para este caso
        // Esperar a que el hijo termine
        while ((flag = wait(&status)) > 0) {
            if (WIFEXITED(status))
                printf("Proceso Padre, Hijo con PID %ld finalizado, status = %d\n", (long int)flag, WEXITSTATUS(status));
            else if (WIFSIGNALED(status))
                printf("Proceso Padre, Hijo con PID %ld finalizado al recibir la señal %d\n", (long int)flag, WTERMSIG(status));
        }
        // Cuando wait devuelve -1 y errno == ECHILD no quedan más hijos
        if (flag == (pid_t)-1 && errno == ECHILD)
            printf("Proceso Padre %d, no hay mas hijos que esperar. Valor de errno = %d, definido como: %s\n", getpid(), errno, strerror(errno));
        else {
            printf("Error en la invocacion de wait. Valor de errno = %d, definido como: %s\n", errno, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
    exit(EXIT_SUCCESS);
}