/** @author Jakub Chomentowski I1ISI */

#include <boost/circular_buffer.hpp>
#include <iostream>

using namespace std;
using namespace boost;

int main(int argc, char *argv[]){

/**Jest to kod przykładowy mający na celu pokazać na przykłądach zastosowanie
 * Szablonu kontenera circula_buffer */

	/**circular_buffer jest szablonem konteneru podobnym do np std::list z tą różnicą że zaimplementowano w nim mechanizmy cyklicznago zapisu.
	Przy tworzeniu go podajemy jakiego rodzaju i jak wiele elementów ma w sobie przechowywać.
	Tu uwaga szablon nie rozszerza się, nie zwiększa objętości zajętej pamięci automatycznie gdy próbujemy zapisać zbyt dużo elementów.*/

	//Utworzenie bufora cyklicznego na 4 liczby int
	circular_buffer<int> buforCykliczny(4);


	//Do bufora można zapisywać dane zrówno od przodu
	buforCykliczny.push_front(1);
	buforCykliczny.push_front(0);

	//Jak i od tyłu
	buforCykliczny.push_back(3);

	
	//Random acces i odczyt danych zapewnia nam operator "[]"
	cout<<"Przyklad wypisywania z bufora liczb"<<endl;
	cout<<buforCykliczny[0]<<endl;		//wypisze 0
	cout<<buforCykliczny[1]<<endl;		//wypisze 1
	cout<<buforCykliczny[2]<<endl;		//wypisze 2
	cout<<endl;


	//Uwaga: Jeśli spróbujemy odczytać coś z poza zakresu <0; size()-1> wystąpi błąd
	//cout<<buforCykliczny[3]<<endl;


	
	//Aby zdjąć element z bufora należy użyć funkcji pop w różnych wariacjach. 
	//Uwaga: Próba zdjęcia elementu z bufora pustego zakończy się rzuceniem wyjątku.

	//pop_front() usówa element z początku
	buforCykliczny.pop_front();			//usuwa pierwszy element
	//a pop_back() z konca
	buforCykliczny.pop_back();			//usuwa ostatni element	

	//teraz także index nam się przesunie. W buforze mamy już tylko jeden element pod indexem 0 i jest to 1
	cout<<"Wypisanie elementow po usunieciu paru sposrod nich"<<endl;
	cout<<buforCykliczny[0]<<endl;		//wypisze 1
	cout<<endl;


	//Jak każdy kontener bazujący na STL circular_buffer zapewnia iteratory umożliwiające przeglądanie swojej zawartości
	//Należy jednak podkreślić że iteratory nie zachowują się cyklicznie toteż betin()-1 i end()+1 są błednę, gdyż wskazują na nieznane miejsce w pamięci
	circular_buffer<int>::iterator it = buforCykliczny.begin();
	cout<<"Wypisanie wartosci zapisanej w iteratorze"<<endl;
	cout<<*it<<endl<<endl;				//wypisze 1

	//Należy zwrócić uwagę iż iterator nie przesunie się automatycznie po wstawieniu kolejnego elementu.
	buforCykliczny.push_front(0);
	cout<<"Wypisanie starego iteratora i nowego poczatku bufora"<<endl;
	cout<<buforCykliczny[0]<<endl;		//wypisze 0
	cout<<*it<<endl;					//wypisze 1
	cout<<*(it - 1)<<endl;				//wypisze 0
	cout<<endl;
	
	
	//"cykliczność" bufora ujawnia się wtedy gdy bufor jest zapełniony
	buforCykliczny.push_back(2);
	buforCykliczny.push_back(3);
	cout<<"Pelny bufor"<<endl;
	cout<<buforCykliczny.capacity()<<endl;	//Wypisze ile może maksymalnie zmieścić się w buforze czyli 4
	cout<<buforCykliczny.size()<<endl;		//Wypisze ile elementów jest wewnątrz bufora czyli 4
	cout<<endl;

	//Gdy dodam do bufora jeszcze jedna liczbę na końcu to nowym początkiem bufora będzie aktualna liczba o indeksie 1
	//a dotychczasowy początek zostanie nadpisany nową wartością.
	buforCykliczny.push_back(4);
	cout<<"Przyklad cyklicznosci"<<endl;
	cout<<*(buforCykliczny.end()-1)<<" "<<buforCykliczny[3]<<endl;			//Wypisze 4 4
	cout<<*(buforCykliczny.begin())<<" "<<buforCykliczny[0]<<endl;			//Wypisze 1 1
	cout<<endl;

	//Oczywiście jeśli sprobujemy nadpisać element używając push_front() analogicznie przesunie nam się koniec bufora
	buforCykliczny.push_front(0);
	cout<<"Przyklad cyklicznosci 2"<<endl;
	cout<<*(buforCykliczny.end()-1)<<" "<<buforCykliczny[3]<<endl;			//Wypisze 3 3
	cout<<*(buforCykliczny.begin())<<" "<<buforCykliczny[0]<<endl;			//Wypisze 0 0
	cout<<endl;

	
	//circular_buffer Ma wbudowane pewne mechanizmy ułatwiające debugowanie. Ma on zapisane wszystkie iteratory do każdego
	//elementu który zwróciła. Gdy jakiś element jest usówany z bufora także iteratory odpowiadające mu są usówane z listy.
	//Jeśli programista spróbuje użyć takiego iteratora zostanie poinformowany przy pomocy assercji o błędzie.
	buforCykliczny.push_back(4);		//Usówa 0 i wstawia na jego miejsce 4
	buforCykliczny.push_back(5);		//Usówa 1 i wstawia na jego miejsce 5
	/*cout<<*it;*/						//Jeśli teraz użyje iteratora it który wskazuje na element 1 wystąpi błąd; 



	//UWAGA: Z RACJI CYKLICZNEJ NATURY BUFORA NIE NALEŻY PRZECHOWYWAĆ W NIM WSKAZAŃ NA AUTOMATYCZNIE ALOKOWANE OBIEKTY, CHYBA ŻE
	//		 UŻYWANE SĄ SPRYTE WSKAŹNIKI!!!

	//UWAGA2: CIRCULAR_BUFFER NIE JEST BEZPIECZNY PRZY WIELOWATKOWOŚCI CHYBA ŻE KAŻDY WĄTEK MA SWOJĄ WŁASNĄ INSTANCJĘ BUFORA LUB
	//		  JEŚLI WĄTKI TYLKO I WYŁĄCZNIE WYKONUJĄ OPERACJĘ CZYTANIA. W PRZECIWNYM RAZIE MOGĄ WYSTĄPIĆ WYŚCIGI.

	//CIEKAWOSTKA:	Istnieje pewna warjacja tego szablonu mianowicie circular_buffer_space_optimized. Działa ona analogicznie do 
	//				zwykłego circular_buffer jednak ma tę istotną różnicę, że nie alokuje od razu bufora o rozmiarze podanym w konstruktorze.
	//				circular_buffer_space_optimized alokuje tylko kawałek potrzebnej pamięci, a gdy ta się przepełni alokuje kolejny kawałek.
	//				Dopiero gdy rozmiar bufora osiągnie zaplanowaną wielkość obiekt przestaje alokować kolejne porcje pamięci i zaczyna
	//				korzystać z mechanizmów cyklicznego zapisu. Może to być przydatne gdy tworzymy duży bufor dużych elementów, ale istnieje
	//				spora szansa że nie uda nam się go przepełnić.
}