/*  
 *   kostra softwareovej aplikacie: 
 *   - master spusta dvoch slave, 
 *   - oba slave plnia zdielany linked-list nahodnou hodnotou z FUNKCIA(),
 *     hodnotu orezu podla vopred definovaneho maximu,
 *     a pridavaju ju ako novu polozku do linkedlistu
 *   - zoznam sa periodicky vypisuje kazdych 5sekund
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
//len pre kostru, funkcia sleep()
#include <unistd.h>
//az bude aplikacne jadro v karte spolupracovat,
// zahrnie sa kniznica na komunikaciu s kartou
//#include <combosix.h>


int FUNKCIA(void)
{
    /* obsah nezaujimavy, funkcionalita:
     *   caka par sekund a nasledne vrati pseudonahodne cislo
     */
    int rnd = rand();
    sleep(rnd % 5);
    return rnd;
}



/* linked list */
struct list {
    int rnd_hodnota; //nahodna hodnota generovana vlaknom, napr. data
    struct list *next;
};

/* struktura odovzdavana vlaknam ako argument */
struct reader_arg {
    int max;  //odlisnost medzi vlaknami, napr. interface
    struct list *list_ptr;  //odkaz na existujici zdielany list
    pthread_mutex_t *list_zamok_ptr; //odkaz na zamok pristupu k zdielanemu listu
};


/* funkcia spustana vo vlaknach */
void *reader(void *arg_void)
{
    struct reader_arg *arg;

    int data;
    struct list *pos;

    struct list *nova_polozka;

    arg = arg_void;

    while (1)
    {
        /* ziskaj novou hodnotu */
        data = FUNKCIA();

        /* osekni ju pod maximum */
        data = data % arg->max;

        /* vytvor novu polozku listu */
        nova_polozka = malloc(sizeof(struct list));
        nova_polozka->rnd_hodnota = data;
        nova_polozka->next = NULL;

        /* zamkni zdielany list,
         * prejdi na koniec listu,
         * zarad novu polozku na koniec,
         * odomkni zdielany list */
        pthread_mutex_lock(arg->list_zamok_ptr);
        pos = arg->list_ptr;
        while (pos->next != NULL)
        {
            pos = pos->next;
        }
        pos->next = nova_polozka;
        pthread_mutex_unlock(arg->list_zamok_ptr);

        /* vrat sa spat hore a nactitaj dalsiu hodnotu */
    }

    return NULL;
}

int main()
{
    //helper pre FUNKCIA()
    srand(time(NULL));

    pthread_t thrds[2];

    /* improvizovana hlavicka listu, je to vlastne prva polozka  v liste
     * s nulovymi hodnotami */
    struct list zdielany_list = { 0, NULL };
    /* musi byt zamknuty pred pristupom k listu
     * a uvolneny (!!!) po dokonceni operacie s listom */
    pthread_mutex_t zdielany_list_zamok = PTHREAD_MUTEX_INITIALIZER;

    /* prve vlakno, chceme hodnoty do max 100000 */
    struct reader_arg first_thrd = {
        .max = 100000,
        .list_ptr = &zdielany_list,
        .list_zamok_ptr = &zdielany_list_zamok,
    };
    if (pthread_create(&thrds[0], NULL, reader, &first_thrd))
        fprintf(stderr, "pthread_create error\n");

    /* druhe vlakno, chceme hodnoty do max 50 */
    struct reader_arg second_thrd = {
        .max = 50,
        .list_ptr = &zdielany_list,
        .list_zamok_ptr = &zdielany_list_zamok,
    };
    if (pthread_create(&thrds[1], NULL, reader, &second_thrd))
        fprintf(stderr, "pthread_create error\n");


    /* "master" pokracuje */

    /* kazdych 5 sekund vypis cely list */
    struct list *pos;
    while (1)
    {
        printf("-------BEGIN-------\n");

        pthread_mutex_lock(&zdielany_list_zamok);
        pos = &zdielany_list;
        while (pos != NULL)
        {
            printf("%d\n", pos->rnd_hodnota);
            pos = pos->next;
        }
        pthread_mutex_unlock(&zdielany_list_zamok);
        
        printf("--------END--------\n");
        sleep(5);
    }


    return 0;
}

