/**
 * @file time_slice.c
 * @brief Time-Slicing GPU Sharing Strategy
 *
 * Multi-Vendor GPU Aggregation Layer for Linux
 *
 * This strategy enables time-sliced GPU sharing across multiple workloads
 * or VMs. It's particularly useful for:
 * - GPU virtualization (vGPU, mediated devices)
 * - Multi-tenant environments
 * - Background/foreground workload separation
 * - Fair resource allocation
 */

#include "../scheduler_internal.h"
#include "mvgal/mvgal_log.h"
#include <time.h>

/* Time-slicing configuration */
struct time_slice_config {
	uint64_t quantum_ns;           /* Time slice quantum in nanoseconds */
	uint32_t max_concurrent;       /* Maximum concurrent workloads */
	bool preemptible;              /* Allow preemption */
	uint32_t priority_boost_ms;    /* Priority boost after preemption */
};

/* Global time-slicing state */
static struct {
	pthread_mutex_t lock;
	struct time_slice_config config;
	
	/* Active workloads tracking */
	struct mvgal_workload *active_workloads[MVGAL_SCHEDULER_MAX_GPUS];
	uint64_t start_time_ns[MVGAL_SCHEDULER_MAX_GPUS];
	uint64_t total_runtime_ns[MVGAL_SCHEDULER_MAX_GPUS];
	
	/* Statistics */
	uint64_t slices_executed;
	uint64_t preemptions;
	uint64_t context_switches;
	
	bool initialized;
} g_time_slice_state = {0};

/**
 * @brief Get current time in nanoseconds
 */
static uint64_t get_time_ns(void) {
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC, &ts);
	return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
}

/**
 * @brief Initialize time-slicing state
 */
static mvgal_error_t time_slice_init(void) {
	if (g_time_slice_state.initialized) {
		return MVGAL_SUCCESS;
	}
	
	pthread_mutex_init(&g_time_slice_state.lock, NULL);
	
	/* Default configuration */
	g_time_slice_state.config.quantum_ns = 16000000; /* 16ms default */
	g_time_slice_state.config.max_concurrent = 4;
	g_time_slice_state.config.preemptible = true;
	g_time_slice_state.config.priority_boost_ms = 5;
	
	memset(g_time_slice_state.active_workloads, 0, 
	       sizeof(g_time_slice_state.active_workloads));
	memset(g_time_slice_state.start_time_ns, 0, 
	       sizeof(g_time_slice_state.start_time_ns));
	memset(g_time_slice_state.total_runtime_ns, 0, 
	       sizeof(g_time_slice_state.total_runtime_ns));
	
	g_time_slice_state.initialized = true;
	
	MVGAL_LOG_INFO("Time-slicing scheduler initialized");
	return MVGAL_SUCCESS;
}

/**
 * @brief Configure time-slicing parameters
 */
mvgal_error_t time_slice_configure(const struct time_slice_config *config) {
	if (config == NULL) {
		return MVGAL_ERROR_INVALID_ARGUMENT;
	}
	
	time_slice_init();
	
	pthread_mutex_lock(&g_time_slice_state.lock);
	g_time_slice_state.config = *config;
	pthread_mutex_unlock(&g_time_slice_state.lock);
	
	MVGAL_LOG_INFO("Time-slicing configured: quantum=%lluns, max_concurrent=%u",
	               config->quantum_ns, config->max_concurrent);
	return MVGAL_SUCCESS;
}

/**
 * @brief Check if a workload has exceeded its time slice
 */
static bool time_slice_expired(uint32_t gpu_index) {
	uint64_t now = get_time_ns();
	uint64_t elapsed = now - g_time_slice_state.start_time_ns[gpu_index];
	
	return elapsed >= g_time_slice_state.config.quantum_ns;
}

/**
 * @brief Distribute workload using time-slicing strategy
 */
mvgal_error_t mvgal_scheduler_distribute_time_slice(struct mvgal_workload *workload) {
	if (workload == NULL) {
		return MVGAL_ERROR_INVALID_ARGUMENT;
	}
	
	time_slice_init();
	
	pthread_mutex_lock(&g_time_slice_state.lock);
	
	/* Find an available GPU or preempt if necessary */
	uint32_t best_gpu = 0xFFFFFFFF;
	uint64_t min_runtime = UINT64_MAX;
	
	for (uint32_t i = 0; i < g_scheduler_state.gpu_count; i++) {
		if (!g_scheduler_state.gpus[i].enabled || !g_scheduler_state.gpus[i].available) {
			continue;
		}
		
		/* Check if GPU is idle */
		if (g_time_slice_state.active_workloads[i] == NULL) {
			best_gpu = i;
			break;
		}
		
		/* Track GPU with minimum runtime for potential preemption */
		if (g_time_slice_state.total_runtime_ns[i] < min_runtime) {
			min_runtime = g_time_slice_state.total_runtime_ns[i];
			best_gpu = i;
		}
	}
	
	if (best_gpu == 0xFFFFFFFF) {
		pthread_mutex_unlock(&g_time_slice_state.lock);
		return MVGAL_ERROR_NO_GPUS;
	}
	
	/* Assign workload to GPU */
	workload->descriptor.assigned_gpu = best_gpu;
	workload->assigned_gpu_count = 1;
	workload->assigned_gpus[0] = best_gpu;
	
	/* Track active workload */
	g_time_slice_state.active_workloads[best_gpu] = workload;
	g_time_slice_state.start_time_ns[best_gpu] = get_time_ns();
	
	g_time_slice_state.slices_executed++;
	
	pthread_mutex_unlock(&g_time_slice_state.lock);
	
	MVGAL_LOG_DEBUG("Time-slice: Workload %u assigned to GPU %u",
	               workload->descriptor.id, best_gpu);
	
	return MVGAL_SUCCESS;
}

/**
 * @brief Check and handle time slice expiration
 * 
 * This should be called periodically by the scheduler to check
 * if any workload has exceeded its time slice.
 */
mvgal_error_t time_slice_check_expirations(void) {
	pthread_mutex_lock(&g_time_slice_state.lock);
	
	for (uint32_t i = 0; i < g_scheduler_state.gpu_count; i++) {
		if (g_time_slice_state.active_workloads[i] != NULL) {
			if (time_slice_expired(i)) {
				/* Time slice expired - mark for preemption */
				struct mvgal_workload *workload = g_time_slice_state.active_workloads[i];
				
				/* Update runtime statistics */
				uint64_t elapsed = get_time_ns() - g_time_slice_state.start_time_ns[i];
				g_time_slice_state.total_runtime_ns[i] += elapsed;
				
				/* Clear active workload */
				g_time_slice_state.active_workloads[i] = NULL;
				
				g_time_slice_state.preemptions++;
				g_time_slice_state.context_switches++;
				
				MVGAL_LOG_DEBUG("Time-slice: Workload %u preempted on GPU %u after %lluns",
				               workload->descriptor.id, i, elapsed);
				
				/* In a real implementation, we would:
				 * 1. Signal the workload to yield
				 * 2. Save GPU state
				 * 3. Schedule next workload
				 */
			}
		}
	}
	
	pthread_mutex_unlock(&g_time_slice_state.lock);
	return MVGAL_SUCCESS;
}

/**
 * @brief Complete a time-sliced workload
 */
mvgal_error_t time_slice_complete_workload(struct mvgal_workload *workload) {
	if (workload == NULL) {
		return MVGAL_ERROR_INVALID_ARGUMENT;
	}
	
	pthread_mutex_lock(&g_time_slice_state.lock);
	
	uint32_t gpu_index = workload->descriptor.assigned_gpu;
	if (gpu_index < MVGAL_SCHEDULER_MAX_GPUS &&
	    g_time_slice_state.active_workloads[gpu_index] == workload) {
		
		/* Update final runtime */
		uint64_t elapsed = get_time_ns() - g_time_slice_state.start_time_ns[gpu_index];
		g_time_slice_state.total_runtime_ns[gpu_index] += elapsed;
		
		/* Clear active workload */
		g_time_slice_state.active_workloads[gpu_index] = NULL;
	}
	
	pthread_mutex_unlock(&g_time_slice_state.lock);
	
	return MVGAL_SUCCESS;
}

/**
 * @brief Get time-slicing statistics
 */
mvgal_error_t time_slice_get_stats(uint64_t *slices, uint64_t *preemptions, 
                                  uint64_t *context_switches) {
	time_slice_init();
	
	pthread_mutex_lock(&g_time_slice_state.lock);
	*slices = g_time_slice_state.slices_executed;
	*preemptions = g_time_slice_state.preemptions;
	*context_switches = g_time_slice_state.context_switches;
	pthread_mutex_unlock(&g_time_slice_state.lock);
	
	return MVGAL_SUCCESS;
}

/**
 * @brief Reset time-slicing state
 */
void time_slice_reset(void) {
	pthread_mutex_lock(&g_time_slice_state.lock);
	
	memset(g_time_slice_state.active_workloads, 0, 
	       sizeof(g_time_slice_state.active_workloads));
	memset(g_time_slice_state.start_time_ns, 0, 
	       sizeof(g_time_slice_state.start_time_ns));
	memset(g_time_slice_state.total_runtime_ns, 0, 
	       sizeof(g_time_slice_state.total_runtime_ns));
	
	g_time_slice_state.slices_executed = 0;
	g_time_slice_state.preemptions = 0;
	g_time_slice_state.context_switches = 0;
	
	pthread_mutex_unlock(&g_time_slice_state.lock);
}