package sd.des;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

/**
 * Gere a Lista de Eventos Futuros (FEL) para Simulação de Eventos Discretos.
 * 
 * <p>A FEL é uma fila de prioridade que mantém todos os eventos futuros agendados,
 * ordenados por timestamp. Este é o coração do paradigma DES - a simulação avança
 * processando eventos em ordem cronológica.</p>
 */
public class EventQueue {
    private final PriorityQueue<SimulationEvent> queue;
    private final List<SimulationEvent> processedEvents;  // For logging and analysis
    private final boolean trackHistory;
    
    public EventQueue() {
        this(true);
    }
    
    public EventQueue(boolean trackHistory) {
        this.queue = new PriorityQueue<>();
        this.processedEvents = trackHistory ? new ArrayList<>() : null;
        this.trackHistory = trackHistory;
    }
    
    /**
     * Agenda um novo evento.
     * 
     * @param event evento a agendar
     */
    public void schedule(SimulationEvent event) {
        queue.offer(event);
    }
    
    /**
     * Agenda um evento com um atraso relativo ao tempo atual.
     * 
     * @param currentTime tempo atual da simulação
     * @param delay atraso em segundos
     * @param type tipo de evento
     * @param payload dados do evento
     * @param location localização do evento
     */
    public void scheduleIn(double currentTime, double delay, DESEventType type, 
                           Object payload, String location) {
        double eventTime = currentTime + delay;
        schedule(new SimulationEvent(eventTime, type, payload, location));
    }
    
    /** Obtém o próximo evento sem o remover */
    public SimulationEvent peek() {
        return queue.peek();
    }
    
    /**
     * Obtém e remove o próximo evento.
     * Se o rastreamento de histórico estiver ativo, adiciona-o aos eventos processados.
     */
    public SimulationEvent poll() {
        SimulationEvent event = queue.poll();
        if (event != null && trackHistory) {
            processedEvents.add(event);
        }
        return event;
    }
    
    /** Verifica se existem eventos pendentes */
    public boolean isEmpty() {
        return queue.isEmpty();
    }
    
    /** @return número de eventos pendentes */
    public int size() {
        return queue.size();
    }
    
    /** Limpa todos os eventos pendentes */
    public void clear() {
        queue.clear();
    }
    
    /**
     * Obtém todos os eventos processados (se o rastreamento estiver ativo).
     * Retorna uma cópia para evitar modificações.
     */
    public List<SimulationEvent> getProcessedEvents() {
        if (!trackHistory) {
            throw new UnsupportedOperationException("History tracking is disabled");
        }
        return new ArrayList<>(processedEvents);
    }
    
    /** @return número de eventos processados */
    public int getProcessedCount() {
        return trackHistory ? processedEvents.size() : 0;
    }
    
    /**
     * Exporta o histórico de eventos para uma string formatada.
     * Útil para debugging e visualização da lista completa de eventos.
     */
    public String exportEventHistory() {
        if (!trackHistory) {
            return "Event history tracking is disabled";
        }
        
        StringBuilder sb = new StringBuilder();
        sb.append("=".repeat(80)).append("\n");
        sb.append("SIMULATION EVENT HISTORY\n");
        sb.append("Total Events Processed: ").append(processedEvents.size()).append("\n");
        sb.append("=".repeat(80)).append("\n");
        sb.append(String.format("%-10s | %-25s | %-20s | %s\n", 
            "Time", "Event Type", "Location", "Details"));
        sb.append("-".repeat(80)).append("\n");
        
        for (SimulationEvent event : processedEvents) {
            String details = event.getPayload() != null ? 
                event.getPayload().getClass().getSimpleName() : "null";
            sb.append(String.format("%-10.3f | %-25s | %-20s | %s\n",
                event.getTimestamp(),
                event.getType(),
                event.getLocation() != null ? event.getLocation() : "N/A",
                details));
        }
        
        return sb.toString();
    }
    
    @Override
    public String toString() {
        return String.format("EventQueue[pending=%d, processed=%d]", 
            queue.size(), getProcessedCount());
    }
}
