/* ******************************* c203.c *********************************** */
/*  Předmět: Algoritmy (IAL) - FIT VUT v Brně                                 */
/*  Úkol: c203 - Fronta znaků v poli                                          */
/*  Referenční implementace: Petr Přikryl, 1994                               */
/*  Přepis do jazyka C: Václav Topinka, září 2005                             */
/*  Úpravy: Kamil Jeřábek, září 2020                                          */
/*          Daniel Dolejška, září 2021                                        */
/*          Daniel Dolejška, září 2022                                        */
/* ************************************************************************** */
/*
** Implementujte frontu znaků v poli. Přesnou definici typů naleznete
** v hlavičkovém souboru c203.h (ADT fronta je reprezentována strukturou Queue,
** která obsahuje pole 'array' pro uložení hodnot ve frontě a indexy firstIndex
** a freeIndex. Všechny implementované funkce musí předpokládat velikost pole
** QUEUE_SIZE, i když ve skutečnosti jsou rozměry statického pole definovány
** MAX_QUEUE. Hodnota QUEUE_SIZE slouží k simulaci fronty v různě velkém poli
** a nastavuje se v testovacím skriptu c203-test.c před testováním
** implementovaných funkcí. Hodnota QUEUE_SIZE může nabývat hodnot v rozsahu
** 1 až MAX_QUEUE.
**
** Index firstIndex ukazuje vždy na první prvek ve frontě. Index freeIndex
** ukazuje na první volný prvek ve frontě. Pokud je fronta prázdná, ukazují
** oba indexy na stejné místo. Po inicializaci ukazují na první prvek pole,
** obsahují tedy hodnotu 0. Z uvedených pravidel vyplývá, že v poli je vždy
** minimálně jeden prvek nevyužitý.
**
** Při libovolné operaci se žádný z indexů (firstIndex i freeIndex) nesnižuje
** vyjma případu, kdy index přesáhne hranici QUEUE_SIZE. V tom případě
** se "posunuje" znovu na začátek pole. Za tímto účelem budete deklarovat
** pomocnou funkci NextIndex, která pro kruhový pohyb přes indexy pole
** využívá operaci "modulo".
**
** Implementujte následující funkce:
**
**    Queue_Init ...... inicializace fronty
**    nextIndex ....... pomocná funkce - viz popis výše
**    Queue_IsEmpty ... test na prázdnost fronty
**    Queue_IsFull .... test, zda je fronta zaplněna (vyčerpána kapacita)
**    Queue_Front ..... přečte hodnoty prvního prvku z fronty
**    Queue_Remove .... odstraní první prvek fronty
**    Queue_Dequeue ... přečte a odstraní první prvek fronty
**    Queue_Enqueue ... zařazení prvku na konec fronty
**
** Nemusíte ošetřovat situaci, kdy místo legálního ukazatele na seznam
** předá někdo jako parametr hodnotu NULL.
**
** Své řešení účelně komentujte!
**
**/

#include "c203.h"

int QUEUE_SIZE = MAX_QUEUE;
bool error_flag;
bool solved;

/**
 * Vytiskne upozornění na to, že došlo k chybě.
 * Tato funkce bude volána z některých dále implementovaných operací.
 *
 * TUTO FUNKCI, PROSÍME, NEUPRAVUJTE!
 *
 * @param error_code Interní identifikátor chyby
 */
void Queue_Error( int error_code ) {
	static const char *QERR_STRINGS[MAX_QERR + 1] = {
			"Unknown error",
			"Queue error: ENQUEUE",
			"Queue error: FRONT",
			"Queue error: REMOVE",
			"Queue error: DEQUEUE",
			"Queue error: INIT"
	};

	if (error_code <= 0 || error_code > MAX_QERR)
	{
		error_code = 0;
	}
	printf("%s\n", QERR_STRINGS[error_code]);
	error_flag = 1;
}

/**
 * Inicializujte frontu následujícím způsobem:
 * - všechny hodnoty v poli queue->array nastavte na '*',
 * - index na začátek fronty nastavte na 0,
 * - index prvního volného místa nastavte také na 0.
 *
 * V případě, že funkce dostane jako parametr queue == NULL, volejte funkci
 * Queue_Error(QERR_INIT).
 *
 * @param queue Ukazatel na strukturu fronty
 */
void Queue_Init( Queue *queue ) {
	if (queue == NULL) 	
		{
        	Queue_Error(QERR_INIT);
        	return;
    	}

    for (int i = 0; i < QUEUE_SIZE; i++) 
		{
        	queue->array[i] = '*';
    	}
    queue->firstIndex = 0;
    queue->freeIndex = 0;	
}

/**
 * Pomocná funkce, která vrací index následujícího prvku v poli.
 * Funkci implementujte jako jediný prikaz využívající operace '%'.
 * Funkci nextIndex budete využívat v dalších implementovaných funkcích.
 *
 * @param index Aktuální index
 */
int nextIndex( int index ) {
	return (index + 1) % QUEUE_SIZE;
}

/**
 * Vrací nenulovou hodnotu, pokud je frona prázdná, jinak vrací hodnotu 0.
 * Funkci je vhodné implementovat jedním příkazem return.
 *
 * @param queue Ukazatel na inicializovanou strukturu fronty
 */
int Queue_IsEmpty( const Queue *queue ) {
	return (queue->firstIndex == queue->freeIndex);
}

/**
 * Vrací nenulovou hodnotu, je-li fronta plná, jinak vrací hodnotu 0.
 * Funkci je vhodné implementovat jedním příkazem return
 * s využitím pomocné funkce nextIndex.
 *
 * @param queue Ukazatel na inicializovanou strukturu fronty
 */
int Queue_IsFull( const Queue *queue ) {
	return (nextIndex(queue->freeIndex) == queue->firstIndex);
}

/**
 * Prostřednictvím parametru dataPtr vrátí znak ze začátku fronty queue.
 * Pokud je fronta prázdná, ošetřete to voláním funkce Queue_Error(QERR_FRONT).
 * Volání této funkce při prázdné frontě je vždy nutné považovat za nekorektní.
 * Bývá to totiž důsledek špatného návrhu algoritmu, ve kterém je fronta
 * použita. O takové situaci se proto musí programátor-vývojář dozvědět.
 * V opačném případě je ladění programů obtížnější!
 *
 * Při implementaci využijte dříve definované funkce Queue_IsEmpty.
 *
 * @param queue Ukazatel na inicializovanou strukturu fronty
 * @param dataPtr Ukazatel na cílovou proměnnou
 */
void Queue_Front( const Queue *queue, char *dataPtr ) {
	if (Queue_IsEmpty(queue)) 
		{
        	Queue_Error(QERR_FRONT);
        	return;
    	}
    *dataPtr = queue->array[queue->firstIndex];
}

/**
 * Odstraní znak ze začátku fronty queue. Pokud je fronta prázdná, ošetřete
 * vzniklou chybu voláním funkce Queue_Error(QERR_REMOVE).
 * Hodnotu na uvolněné pozici ve frontě nijak neošetřujte (nepřepisujte).
 * Při implementaci využijte dříve definované funkce Queue_IsEmpty a nextIndex.
 *
 * @param queue Ukazatel na inicializovanou strukturu fronty
 */
void Queue_Remove( Queue *queue ) {
	if (Queue_IsEmpty(queue)) 
		{
        	Queue_Error(QERR_REMOVE);
        	return;
    	}
    queue->firstIndex = nextIndex(queue->firstIndex);
}

/**
 * Odstraní znak ze začátku fronty a vrátí ho prostřednictvím parametru dataPtr.
 * Pokud je fronta prázdná, ošetřete to voláním funkce Queue_Error(QERR_DEQUEUE).
 *
 * Při implementaci využijte dříve definovaných funkcí Queue_IsEmpty,
 * Queue_Front a Queue_Remove.
 *
 * @param queue Ukazatel na inicializovanou strukturu fronty
 * @param dataPtr Ukazatel na cílovou proměnnou
 */
void Queue_Dequeue( Queue *queue, char *dataPtr ) {
	if (Queue_IsEmpty(queue)) 
		{
        	Queue_Error(QERR_DEQUEUE);
        	return;
    	}
    Queue_Front(queue, dataPtr);
    Queue_Remove(queue);
}

/**
 * Vloží znak data do fronty. Pokud je fronta plná, ošetřete chybu voláním
 * funkce Queue_Error(QERR_ENQUEUE). Vkládání do plné fronty se považuje za
 * nekorektní operaci. Situace by mohla být řešena i tak, že by operace
 * neprováděla nic, ale v případě použití takto definované abstrakce by se
 * obtížně odhalovaly chyby v algoritmech, které by abstrakci využívaly.
 *
 * Při implementaci využijte dříve definovaných funkcí Queue_IsFull a nextIndex.
 *
 * @param queue Ukazatel na inicializovanou strukturu fronty
 * @param data Znak k vložení
 */
void Queue_Enqueue( Queue *queue, char data ) {
	if (Queue_IsFull(queue)) 
		{
        	Queue_Error(QERR_ENQUEUE);
        	return;
    	}
    queue->array[queue->freeIndex] = data;
    queue->freeIndex = nextIndex(queue->freeIndex);
}

/* Konec příkladu c203.c */
