#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "csim.h"

#define NUM_CLIENTS 5
#define DB_SIZE 500
#define DATA_ITEM_SIZE 1024
#define BANDWIDTH 10000
#define CACHE_SIZE 100
#define NUM_HOT_ITEMS 50
#define SIMULATION_TIME 10e4

double t_query_arr[] = { 5, 10, 25, 50, 100 };
double t_update_arr[] = { 5, 10, 20, 100, 200 };
float hot_access_prob = 0.8;
float hot_update_prob = 0.33;

typedef struct {
	int id;
	int cache[CACHE_SIZE];
	int cache_age[CACHE_SIZE]; // Add this field to store age of each item
	double q_gen_time;
	double u_arr_time;
} node;


FACILITY server;
TABLE query_delay, cache_hit_ratio;
node* nodes;
float total_queries = 0;
float cache_hits = 0;

void client();
void generate_query(node* n);
void query_result(node* n);
int is_item_in_cache(node* n, int item);


void update_cache(node* n, int data_item) {
	int min_age_index = 0;
	int i = 0;
	for (; i < CACHE_SIZE; i++) {
		if (n->cache[i] == -1) { // Empty cache slot found
			n->cache[i] = data_item;
			n->cache_age[i] = 1;
			return;
		}

		if (n->cache_age[i] < n->cache_age[min_age_index]) {
			min_age_index = i;
		}
	}

	// Replace the least recently used item in the cache
	n->cache[min_age_index] = data_item;
	n->cache_age[min_age_index] = 1;
}


int main() {
	server = create_facility("Server");
	create("simple_pull_based_cache");
	nodes = (node*)malloc(NUM_CLIENTS * sizeof(node));

	table_init("Query Delay", &query_delay);
	table_init("Cache Hit Ratio", &cache_hit_ratio);

	// Simulating
	for (size_t q = 0; q < sizeof(t_query_arr) / sizeof(t_query_arr[0]); q++) {
		for (size_t u = 0; u < sizeof(t_update_arr) / sizeof(t_update_arr[0]); u++) {
			reset();

			total_queries = 0;
			cache_hits = 0;

			for (int i = 0; i < NUM_CLIENTS; i++) {
				nodes[i].id = i;
				nodes[i].q_gen_time = t_query_arr[q];
				nodes[i].u_arr_time = t_update_arr[u];
				// Initialize cache

				for (int j = 0; j < CACHE_SIZE; j++) {
					nodes[i].cache[j] = -1;
					nodes[i].cache_age[j] = -1; // Initialize age of each item to -1
				}
				node_activity(client);
			}

			schedule(START, 0.0);
			schedule(END, SIMULATION_TIME);
			simulate();

			printf("Cache hit ratio: %.2f\n", cache_hits / total_queries);
			printf("Query delay: %.2f\n", tbl_mean(query_delay));
			printf("\n");
		}
	}

	// Clean up
	table_rm(query_delay);
	table_rm(cache_hit_ratio);
	facility_rm(server);
	cleanup();
	free(nodes);

	return 0;
}

void client() {
	node* n = &nodes[node_id()];
	while (clock < SIMULATION_TIME) {
		hold(exponential(n->q_gen_time));

		float prob = lrand48() / (float)RAND_MAX;

		// Hot data item or cold data item
		int data_item;
		if (prob < hot_access_prob) {
			data_item = lrand48() % NUM_HOT_ITEMS + 1;
		}
		else {
			data_item = NUM_HOT_ITEMS + (lrand48() % (DB_SIZE - NUM_HOT_ITEMS));
		}

		generate_query(n);
	}
}

void generate_query(node* n) {
	request(server);
	double q_start_time = clock;
	hold(DATA_ITEM_SIZE * 8 / BANDWIDTH);
	int cache_hit = is_item_in_cache(n, data_item);

	if (cache_hit) {
		cache_hits++;
		release(server);

		// Send a check message to the server
		request(server);
		hold(DATA_ITEM_SIZE * 8 / BANDWIDTH);

		// Receive confirm message from the server
		release(server);

	}
	else {
		// Request data item from the server
		release(server);
		query_result(n);
	}

	// Calculate query delay
	double q_delay = clock - q_start_time;
	sample(query_delay, q_delay);

	total_queries++;
}


void query_result(node* n) {
	hold(exponential(n->u_arr_time));

	// Probability of hot data item update
	float prob = lrand48() / (float)RAND_MAX;

	if (prob < hot_update_prob) {
		request(server);
		hold(DATA_ITEM_SIZE * 8 / BANDWIDTH);

		// Update hot data item
		release(server);
	}

	// Update the cache based on the LRU policy
	update_cache(n, data_item);
}
int is_item_in_cache(node* n, int item) {
	int item_found = 0;
	for (int i = 0; i < CACHE_SIZE; i++) {
		if (n->cache[i] == item) {
			n->cache_age[i] = 1;
			item_found = 1;
		}
		else {
			n->cache_age[i]++;
		}
	}
	return item_found;
}
//void handle_csim_error_message(long err) {
//	if (err != 0) {
//		printf("Error: %s\n", csim_error_message[err]);
//		exit(1);
//	}
//}

data_item is undefined error, please fix, only show me the functions which you are going to update