// WARNING: this code does not check the return value of system calls
//          I removed the error checking so it is easier to see the 
//          logic of the program.
// 
//          YOU SHOULD ALWAYS CHECK THE RETURN VALUE OF SYSTEM CALLS 
// 
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
using namespace std;

int sharks = 0;
int kids = 0;

pthread_mutex_t mutex =  PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t print_mutex =  PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t tank = PTHREAD_COND_INITIALIZER;

void *kid_code(void *arg)
{
    for (int iteration = 0; iteration < 5; iteration++)
    {
        int id = (int) arg;
    
        pthread_mutex_lock(&mutex);
        {
            while (sharks != 0)
                pthread_cond_wait(&tank, &mutex);
            kids++;
            if (kids == 1)
                pthread_cond_broadcast(&tank);
            pthread_mutex_unlock(&mutex);
        } 
        // swim
        {
            int interval = rand() %3 + 1;
            pthread_mutex_lock(&print_mutex);
            {
                cout << "kid " << id << " swimming for " <<interval<<" seconds" 
                     << " kids = " << kids 
                     << " sharks = " <<  sharks << endl;
            }
            pthread_mutex_unlock(&print_mutex);
    
            sleep(interval);
    
        }
        pthread_mutex_lock(&mutex);
        {
            kids--;
            if (kids == 0)
                pthread_cond_broadcast(&tank);
        }
        pthread_mutex_unlock(&mutex);

        // eat
        sleep(rand() %5 + 1);
    }
    return 0;
}
void *shark_code(void *arg)
{
    for (int iteration = 0; iteration < 5; iteration++)
    {
        int id = (int) arg;
    
        pthread_mutex_lock(&mutex);
        {
            while (kids != 0)
                pthread_cond_wait(&tank, &mutex);
            sharks++;
            if (sharks == 0)
                pthread_cond_broadcast(&tank);
        }
        pthread_mutex_unlock(&mutex);
    
        // swim
        {
            int interval = rand() %3 + 1;
            pthread_mutex_lock(&print_mutex);
            {
                cout << "shark " <<id<< " swimming for " <<interval<< " seconds"
                     << " kids = " << kids << " sharks = " << sharks << endl;
            }
            pthread_mutex_unlock(&print_mutex);
    
            sleep(interval);
    
        }
        pthread_mutex_lock(&mutex);
        {
            sharks--;
            if (sharks == 0)
            {
                pthread_cond_broadcast(&tank);
            }
        }
        pthread_mutex_unlock(&mutex);

        // eat
        sleep(rand() %5 + 1);
    }
    return 0;
}

int main()
{
    const int num_swimmers = 10;

    pthread_t swimmers[num_swimmers];
    pthread_mutex_lock(&mutex);
    cout << "parent creating swimmers" << endl;
    for (int i = 0; i < num_swimmers; i++)
    {
        if (rand() & 1)
            pthread_create(&swimmers[i], NULL, kid_code, (void *) i);
        else pthread_create(&swimmers[i], NULL, shark_code, (void *) i);
    }
    pthread_mutex_unlock(&mutex);

    for (int i = 0; i < num_swimmers; i++)
        pthread_join(swimmers[i], NULL);
    cout << "parent after all swimmers have quit" << endl;
}

