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

// Define the Process structure to hold scheduling details.
typedef struct {
    int pid;         // Process ID for display.
    int arrival;     // Arrival time.
    int burst;       // Burst time (total execution time).
    int waiting;     // Waiting time (calculated).
    int turnaround;  // Turnaround time (calculated).
    int completion;  // Completion time (calculated).
    int remaining;   // Remaining burst time (for preemptive SJF).
} Process;

// Function: fcfs_scheduling
// Implements the First-Come, First-Served scheduling algorithm.
void fcfs_scheduling(Process proc[], int n) {
    // Sort processes by arrival time using bubble sort.
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (proc[j].arrival > proc[j + 1].arrival) {
                Process temp = proc[j];
                proc[j] = proc[j + 1];
                proc[j + 1] = temp;
            }
        }
    }

    int current_time = 0;  // Simulated current time.
    for (int i = 0; i < n; i++) {
        // If the CPU is idle, fast-forward current time to the arrival of the next process.
        if (current_time < proc[i].arrival) {
            current_time = proc[i].arrival;
        }
        // Waiting time is the time process has to wait from its arrival.
        proc[i].waiting = current_time - proc[i].arrival;
        // Turnaround time is waiting time plus burst time.
        proc[i].turnaround = proc[i].waiting + proc[i].burst;
        // Completion time is when the process finishes execution.
        proc[i].completion = current_time + proc[i].burst;
        // Update current time to include the burst time of this process.
        current_time += proc[i].burst;
    }

    // Print the FCFS scheduling results.
    printf("\n=== FCFS Scheduling Results (Child Process) ===\n");
    printf("PID\tArrival\tBurst\tCompletion\tWaiting\tTurnaround\n");
    for (int i = 0; i < n; i++) {
        printf("%d\t%d\t%d\t%d\t\t%d\t%d\n",
            proc[i].pid,
            proc[i].arrival,
            proc[i].burst,
            proc[i].completion,
            proc[i].waiting,
            proc[i].turnaround);
    }
}

// Function: preemptive_sjf
// Implements the Preemptive SJF (Shortest Job First) scheduling algorithm.
void preemptive_sjf(Process proc[], int n) {
    // Initialize remaining time for each process.
    for (int i = 0; i < n; i++) {
        proc[i].remaining = proc[i].burst;
    }

    int complete = 0;      // Number of completed processes.
    int current_time = 0;  // Simulated current time.
    int smallest;          // Index of process with smallest remaining time.
    int finish_time;       // Time when a process completes.
    int found;             // Flag indicating if a process is found at current time.

    // Process scheduling until all processes complete.
    while (complete != n) {
        found = 0;
        smallest = -1;
        // Find process with minimum remaining time that has arrived.
        for (int i = 0; i < n; i++) {
            if (proc[i].arrival <= current_time && proc[i].remaining > 0) {
                if (smallest == -1 || proc[i].remaining < proc[smallest].remaining) {
                    smallest = i;
                    found = 1;
                }
            }
        }

        // If no process is available at current_time, increment time.
        if (!found) {
            current_time++;
            continue;
        }

        // Execute the selected process for one time unit.
        proc[smallest].remaining--;
        current_time++;

        // When a process completes execution, update its times.
        if (proc[smallest].remaining == 0) {
            complete++;
            finish_time = current_time;
            // Completion time is the finish time.
            proc[smallest].completion = finish_time;
            // Turnaround time is finish time minus arrival time.
            proc[smallest].turnaround = finish_time - proc[smallest].arrival;
            // Waiting time is turnaround time minus burst time.
            proc[smallest].waiting = proc[smallest].turnaround - proc[smallest].burst;
        }
    }

    // Print the Preemptive SJF scheduling results.
    printf("\n=== Preemptive SJF Scheduling Results (Parent Process) ===\n");
    printf("PID\tArrival\tBurst\tCompletion\tWaiting\tTurnaround\n");
    for (int i = 0; i < n; i++) {
        printf("%d\t%d\t%d\t%d\t\t%d\t%d\n",
            proc[i].pid,
            proc[i].arrival,
            proc[i].burst,
            proc[i].completion,
            proc[i].waiting,
            proc[i].turnaround);
    }
}

int main() {
    int n;

    // Prompt user to input the number of processes.
    printf("Enter the number of processes: ");
    fflush(stdout);  // Ensure the prompt is displayed.
    scanf("%d", &n);

    // Declare two arrays for process data:
    // - One for the child (FCFS) and one for the parent (Preemptive SJF).
    Process processes[n];
    Process processes_copy[n]; // Copy for parent's use.

    // Loop to gather process details.
    for (int i = 0; i < n; i++) {
        processes[i].pid = i + 1;  // Process IDs start at 1.

        // Input arrival time.
        printf("Enter arrival time for process %d: ", i + 1);
        fflush(stdout);
        scanf("%d", &processes[i].arrival);

        // Input burst time.
        printf("Enter burst time for process %d: ", i + 1);
        fflush(stdout);
        scanf("%d", &processes[i].burst);

        // Copy process data for the parent process scheduling.
        processes_copy[i] = processes[i];
    }

    // Fork the process to split execution into two scheduling algorithms.
    pid_t pid = fork();

    if (pid < 0) {
        // Error handling for fork failure.
        perror("Fork failed");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0) {
        // Child process: Execute FCFS scheduling.
        printf("\nChild process executing FCFS scheduling...\n");
        fcfs_scheduling(processes, n);
        exit(0);  // End child process.
    }
    else {
        // Parent process: Wait for child to finish and then execute Preemptive SJF.
        wait(NULL);
        printf("\nParent process executing Preemptive SJF scheduling...\n");
        preemptive_sjf(processes_copy, n);
    }

    return 0;
}