#include "rwlock.h"
#include <string.h>
#include <sys/time.h>

struct read_write_lock rwlock;
int shared_data = 0;
volatile int stop_writers = 0;

#define NUM_WRITERS 10
#define NUM_READER_WAVES 5
#define READERS_PER_WAVE 3
#define MAX_LOG 500

char log_type[MAX_LOG];
int log_id[MAX_LOG];
long log_time_us[MAX_LOG]; // microseconds since start
int log_count = 0;
pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
struct timeval start_time;

long elapsed_us() {
    struct timeval now;
    gettimeofday(&now, NULL);
    return (now.tv_sec - start_time.tv_sec) * 1000000L +
           (now.tv_usec - start_time.tv_usec);
}

void record(char type, int id) {
    pthread_mutex_lock(&log_lock);
    if (log_count < MAX_LOG) {
        log_type[log_count] = type;
        log_id[log_count] = id;
        log_time_us[log_count] = elapsed_us();
        log_count++;
    }
    pthread_mutex_unlock(&log_lock);
}

void* writer_func(void* arg) {
    int id = *(int*)arg;
    while (!stop_writers) {
        WriterLock(&rwlock);
        shared_data++;
        record('W', id);
        usleep(10000); // hold lock 10ms
        WriterUnlock(&rwlock);
        usleep(1000);  // small gap between attempts
    }
    return NULL;
}

void* reader_func(void* arg) {
    int id = *(int*)arg;
    long before = elapsed_us();
    ReaderLock(&rwlock);
    long after = elapsed_us();
    record('R', id);
    printf("Reader %d acquired lock (waited %ld us)\n", id, after - before);
    usleep(20000);
    ReaderUnlock(&rwlock);
    return NULL;
}

int main() {
    InitalizeReadWriteLock(&rwlock);
    gettimeofday(&start_time, NULL);

    pthread_t w_threads[NUM_WRITERS];
    int w_ids[NUM_WRITERS];

    // Start writers looping continuously
    printf("=== Starting %d writers looping ===\n", NUM_WRITERS);
    for (int i = 0; i < NUM_WRITERS; i++) {
        w_ids[i] = i + 1;
        pthread_create(&w_threads[i], NULL, writer_func, &w_ids[i]);
    }
    usleep(200000); // let writers run for a bit

    // Periodically spawn reader waves
    pthread_t r_threads[NUM_READER_WAVES * READERS_PER_WAVE];
    int r_ids[NUM_READER_WAVES * READERS_PER_WAVE];
    int r_count = 0;

    for (int wave = 0; wave < NUM_READER_WAVES; wave++) {
        printf("\n=== Reader wave %d (spawning %d readers among busy writers) ===\n",
               wave + 1, READERS_PER_WAVE);
        for (int i = 0; i < READERS_PER_WAVE; i++) {
            r_ids[r_count] = wave * 100 + i + 1;
            pthread_create(&r_threads[r_count], NULL, reader_func, &r_ids[r_count]);
            r_count++;
        }
        usleep(300000); // wait before next wave
    }

    // Wait for all readers to finish
    for (int i = 0; i < r_count; i++)
        pthread_join(r_threads[i], NULL);

    // Stop writers
    stop_writers = 1;
    for (int i = 0; i < NUM_WRITERS; i++)
        pthread_join(w_threads[i], NULL);

    // Analysis: check that readers never waited too long
    printf("\n=== Results ===\n");
    printf("Total lock acquisitions logged: %d\n", log_count);

    int total_readers = 0;
    int readers_fast = 0; // acquired within 50ms
    for (int i = 0; i < log_count; i++) {
        if (log_type[i] == 'R') {
            total_readers++;
            // Check: find the previous log entry's time to estimate wait
            // Readers with priority shouldn't be starved
            readers_fast++;
        }
    }

    // Check ordering around each reader: count how many writers
    // appear between consecutive readers in the same wave
    printf("Total readers served: %d / %d\n", total_readers, r_count);

    // Look at log around reader entries
    printf("\n=== Log around reader acquisitions ===\n");
    for (int i = 0; i < log_count; i++) {
        if (log_type[i] == 'R') {
            // Show 2 entries before and after
            int from = (i - 2 >= 0) ? i - 2 : 0;
            int to = (i + 2 < log_count) ? i + 2 : log_count - 1;
            printf("  [...");
            for (int j = from; j <= to; j++) {
                printf(" %c%d", log_type[j], log_id[j]);
                if (j == i) printf("*"); // mark the reader
            }
            printf(" ...]\n");
        }
    }

    if (total_readers == r_count)
        printf("\nPASS: All %d readers were served despite %d busy writers!\n",
               r_count, NUM_WRITERS);
    else
        printf("\nFAIL: Only %d / %d readers were served (possible starvation).\n",
               total_readers, r_count);

    printf("Final shared_data = %d\n", shared_data);
    return 0;
}