/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   main.c                                             :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: thrieg <thrieg@student.42mulhouse.fr>      +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2025/12/11 04:07:30 by thrieg            #+#    #+#             */
/*   Updated: 2025/12/11 04:59:32 by thrieg           ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "../includes/ft_strace.h"

int main_loop(size_t binary_type)
{
    int in_syscall = 0;
    while (1) {
        // Ask kernel to run until next syscall or signal
        if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) break;

        int status;
        if (waitpid(pid, &status, 0) == -1) break;

        if (WIFEXITED(status)) {
            // child exited normally
            printf("+++ exited with %d +++\n", WEXITSTATUS(status));
            break;
        }
        if (WIFSIGNALED(status)) {
            // killed by a signal
            printf("+++ killed by %s +++\n", strsignal(WTERMSIG(status)));
            break;
        }
        if (!WIFSTOPPED(status))
            continue;

        int sig = WSTOPSIG(status);

        if (sig == (SIGTRAP | 0x80)) {
            // syscall-enter or syscall-exit
            if (!in_syscall) {
                // ENTRY
                read_regs_and_print_entry(pid);
                in_syscall = 1;
            } else {
                // EXIT
                read_regs_and_print_exit(pid);
                in_syscall = 0;
            }
        } else {
            // A real signal: print it, then let it continue
            print_signal(pid, sig);
            // pass down the signal to the actual program
            if (ptrace(PTRACE_SYSCALL, pid, 0, sig) == -1) break;
            waitpid(pid, &status, 0);
        }
    }
}

int main(int argc, char **argv)
{
    if (argc < 2)
    {
        dprintf(2, "usage: %s <path/to/program> <arg1> <arg2> ...\n", argv[0]);
        return (EXIT_FAILURE);
    }

    //determine if this file is an executable, and if so, 64 or 32 bits
    ssize_t binary_bits = binary_type(argv[1]);
    if (binary_bits < 0)
    {
        if (binary_bits == -1)
        {
            dprintf(2, "couldn't read that file, make sure you have the read and execute permissions on %s\n", argv[1]);
        }
        else if (binary_bits == -2)
        {
            dprintf(2, "%s is not a supported binary\n", argv[1]);
        }
        return (EXIT_FAILURE);
    }

    pid_t pid = fork();

    if (pid == 0) {
        execvp(argv[1], argv + 1);
        perror("execvp");
        return (EXIT_FAILURE);
    }
    else {
        // 1) Seize the child
        if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) perror("PTRACE_SEIZE");

        // 2) Interrupt it to force a stop
        if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) perror("PTRACE_INTERRUPT");

        // 3) Wait for it to stop
        int status;
        waitpid(pid, &status, 0);

        // 4) Set options
        long opts = PTRACE_O_TRACESYSGOOD;
        ptrace(PTRACE_SETOPTIONS, pid, 0, opts);

        // 5) Enter main trace loop
        return (main_loop((size_t)binary_bits));
    }
    return (EXIT_SUCCESS);
}