#define _GNU_SOURCE
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#define SIG SIGRTMIN
#define WATCHDOG_SIG SIGRTMIN+1

void watchdog_handler(int sig, siginfo_t *si, void *uc) {
    printf("Watchdog: przekroczono limit czasu!\n");
}

void *thread1(void *arg) {
    sigset_t set;
    int sig;
    timer_t timer1, watchdog_timer;
    struct itimerspec its1, its_watchdog;
    struct sigevent sev1, sev_watchdog;
    struct timespec now;

    sigemptyset(&set);
    sigaddset(&set, SIG);

    sev1.sigev_notify = SIGEV_SIGNAL;
    sev1.sigev_signo = SIG;
    timer_create(CLOCK_REALTIME, &sev1, &timer1);

    sev_watchdog.sigev_notify = SIGEV_SIGNAL;
    sev_watchdog.sigev_signo = WATCHDOG_SIG;
    timer_create(CLOCK_REALTIME, &sev_watchdog, &watchdog_timer);

    clock_gettime(CLOCK_REALTIME, &now);
    its1.it_value.tv_sec = now.tv_sec + 2; // 2 sekundy opóżnienia
    its1.it_value.tv_nsec = now.tv_nsec;
    its1.it_interval.tv_sec = 3; // 1-dnosekudowy interwał
    its1.it_interval.tv_nsec = 0;

    timer_settime(timer1, TIMER_ABSTIME, &its1, NULL);

    while (1) {
        sigwait(&set, &sig);
        printf("Watek 1: obliczenia wznowione\n");

        // Dodajemy tutaj watchdog timer
        clock_gettime(CLOCK_REALTIME, &now);
        its_watchdog.it_value.tv_sec = now.tv_sec + 3; // 3 sekundy opóźnienia
        its_watchdog.it_value.tv_nsec = now.tv_nsec;
        its_watchdog.it_interval.tv_sec = 0; // bez interwału
        its_watchdog.it_interval.tv_nsec = 0;

        timer_settime(watchdog_timer, TIMER_ABSTIME, &its_watchdog, NULL);
        //------------------------------------------------------------------
        //Czas na wykonnywanie obliczeń w pojedynczym cyklu:
        //------------------------------------------------------------------
        sleep(2);
        its_watchdog.it_value.tv_sec = 0;
        its_watchdog.it_value.tv_nsec = 0;
        its_watchdog.it_interval.tv_sec = 0;
        its_watchdog.it_interval.tv_nsec = 0;
        timer_settime(watchdog_timer, TIMER_ABSTIME, &its_watchdog, NULL);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t t1;
    pthread_attr_t attr1;
    struct sched_param param1, param_main;
    cpu_set_t cpuset;

    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIG);
    pthread_sigmask(SIG_BLOCK, &set, NULL);

    struct sigaction sa;
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = watchdog_handler;
    sigaction(WATCHDOG_SIG, &sa, NULL);

    CPU_ZERO(&cpuset);
    CPU_SET(1, &cpuset); // Wybież procesor nr 2 w systemie
    printf("Liczba dostepnych CPU: %d\n", (int) sysconf(_SC_NPROCESSORS_ONLN));

    param_main.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3;
    pthread_setschedparam(pthread_self(), SCHED_FIFO, &param_main);
    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);

    pthread_attr_init(&attr1);
    pthread_attr_setscope(&attr1, PTHREAD_SCOPE_SYSTEM);
    pthread_attr_setinheritsched(&attr1, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
    param1.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
    pthread_attr_setschedparam(&attr1, &param1);
    pthread_attr_setaffinity_np(&attr1, sizeof(cpu_set_t), &cpuset);
    pthread_create(&t1, &attr1, thread1, NULL);

    pthread_join(t1, NULL);

    pthread_attr_destroy(&attr1);

    return 0;
}
