/*
 *  Předmět: Algoritmy (IAL) - FIT VUT v Brně
 *  Rozšíření pro příklad c206.c (Dvousměrně vázaný lineární seznam)
 *  Vytvořil: Daniel Dolejška, září 2024
 */

#include "c206-ext.h"

bool error_flag;
bool solved;

/**
 * Tato metoda simuluje příjem síťových paketů s určenou úrovní priority.
 * Přijaté pakety jsou zařazeny do odpovídajících front dle jejich priorit.
 * "Fronty" jsou v tomto cvičení reprezentovány dvousměrně vázanými seznamy
 * - ty totiž umožňují snazší úpravy pro již zařazené položky.
 * 
 * Parametr `packetLists` obsahuje jednotlivé seznamy paketů (`QosPacketListPtr`).
 * Pokud fronta s odpovídající prioritou neexistuje, tato metoda ji alokuje
 * a inicializuje. Za jejich korektní uvolnení odpovídá volající.
 * 
 * V případě, že by po zařazení paketu do seznamu počet prvků v cílovém seznamu
 * překročil stanovený MAX_PACKET_COUNT, dojde nejdříve k promazání položek seznamu.
 * V takovémto případě bude každá druhá položka ze seznamu zahozena nehledě
 * na její vlastní prioritu ovšem v pořadí přijetí.
 * 
 * @param packetLists Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
 * @param packet Ukazatel na strukturu přijatého paketu
 */
void receive_packet(DLList *packetLists, PacketPtr packet) {
	QosPacketListPtr targetList = NULL;
    DLL_First(packetLists);
    while (DLL_IsActive(packetLists)) {
        QosPacketListPtr currentList = (QosPacketListPtr)((long)packetLists->activeElement->data);
        if (currentList->priority == packet->priority) {
            targetList = currentList;
            break;
        }
        if (currentList->priority < packet->priority) {
            break;
        }
        DLL_Next(packetLists);
    }

    if (targetList == NULL) {
        
        targetList = (QosPacketListPtr)malloc(sizeof(QosPacketList));
        if (targetList == NULL) {
            error_flag = true;
            return;
        }
        targetList->priority = packet->priority;
        targetList->list = (DLList *)malloc(sizeof(DLList));
        if (targetList->list == NULL) {
            free(targetList);
            error_flag = true;
            return;
        }
        DLL_Init(targetList->list);
        

        if (!DLL_IsActive(packetLists)) {
            DLL_InsertLast(packetLists, (long)targetList);
        } else {
            DLL_InsertBefore(packetLists, (long)targetList);
        }
    }

    if (targetList->list->currentLength >= MAX_PACKET_COUNT) {
        DLLElementPtr current = targetList->list->firstElement;
        DLLElementPtr next;
        int count = 0;
        while (current != NULL) {
            next = current->nextElement;
            if (count % 2 == 1) {
                
                if (current == targetList->list->activeElement) {
                    targetList->list->activeElement = next;
                }
                if (current == targetList->list->firstElement) {
                    targetList->list->firstElement = next;
                }
                if (current == targetList->list->lastElement) {
                    targetList->list->lastElement = current->previousElement;
                }
                if (current->previousElement) {
                    current->previousElement->nextElement = next;
                }
                if (next) {
                    next->previousElement = current->previousElement;
                }
                free(current);
                targetList->list->currentLength--;
            }
            current = next;
            count++;
        }
    }

    
    DLL_InsertLast(targetList->list, (long)packet);
}

/**
 * Tato metoda simuluje výběr síťových paketů k odeslání. Výběr respektuje
 * relativní priority paketů mezi sebou, kde pakety s nejvyšší prioritou
 * jsou vždy odeslány nejdříve. Odesílání dále respektuje pořadí, ve kterém
 * byly pakety přijaty metodou `receive_packet`.
 * 
 * Odeslané pakety jsou ze zdrojového seznamu při odeslání odstraněny.
 * 
 * Parametr `packetLists` obsahuje ukazatele na jednotlivé seznamy paketů (`QosPacketListPtr`).
 * Parametr `outputPacketList` obsahuje ukazatele na odeslané pakety (`PacketPtr`).
 * 
 * @param packetLists Ukazatel na inicializovanou strukturu dvousměrně vázaného seznamu
 * @param outputPacketList Ukazatel na seznam paketů k odeslání
 * @param maxPacketCount Maximální počet paketů k odeslání
 */
void send_packets(DLList *packetLists, DLList *outputPacketList, int maxPacketCount) {
	int sentCount = 0;
    
    while (sentCount < maxPacketCount) {
        
        DLL_First(packetLists);
        
        while (DLL_IsActive(packetLists) && sentCount < maxPacketCount) {
            QosPacketListPtr currentList = (QosPacketListPtr)((long)packetLists->activeElement->data);
            
            if (currentList->list->currentLength > 0) {
                
                PacketPtr packet = (PacketPtr)((long)currentList->list->firstElement->data);
                DLL_InsertLast(outputPacketList, (long)packet);
                DLL_DeleteFirst(currentList->list);
                sentCount++;
            } else {
                
                DLL_Next(packetLists);
            }
        }
        
        if (!DLL_IsActive(packetLists)) {
            
            break;
        }
    }
}

