/* Paweł Kęsik, grupa E1ISIII
ZPR praca domowa
Przykład wykorzystania C++ std::locale
 
Przykład ten przedstawia użycie podstawowych funkcji lokalizacji dostępnych w bibliotece standardowej języka C++ 

Uwaga: Usługi lokalizacyjne są zależne od danej implementacji i mogą działać w różny sposób na różnych platformach.
Przykładowo format daty w lokalizacji polskiej może być różny na systemach Windows i Linux.
Zakres obsługiwanych lokalizacji jest również zależny od platformy.
Ten przykład był testowany na serwerze galera.ii.pw.edu.pl (Red Hat Linux 4.1.2-13)*/

#include <iostream>
#include <sstream>
#include <locale> //plik nagłówkowy wymagany przez usługi lokalizacyjne
#include <vector>
#include <ctime>
#include <stdexcept>

using namespace std;

typedef vector<locale> LocVector;
typedef vector<locale>::const_iterator LocVectorIter;
typedef istreambuf_iterator<char,char_traits<char> > IstreamIter;
typedef ostreambuf_iterator<char,char_traits<char> > OstreamIter;

int main()
{
	LocVector locVector;

	/*Utworzenie domyślnego obiektu lokalizacji - obiekt ten jest skojarzony z ustawieniami
	językowymi charakterystycznymi dla systemu operacyjnego
	Tworzymy go podając pusty ciąg znaków jako argument konstruktora klasy locale */
	locale defaultLoc("");

	try
	{
		// tworzenie innych obiektów lokalizacji
		locVector.push_back(locale());	//lokalizacja globalna - standardowe US English ASCII
		locVector.push_back(locale("dutch")); //lokalizacja dla języka holenderskiego
		locVector.push_back(locale("polish")); //lokalizacja dla języka polskiego	
		locVector.push_back(locale("german")); //lokalizacja dla języka niemieckiego
		locVector.push_back(locale("swedish")); //lokalizacja dla języka szwedzkiego
		
		//Uwaga: Nazwy obiektów lokalizacji (np. polish, german) nie są ustandaryzowane i mogą
		//być inne w zależnosći od danej implementacji i systemu operacyjnego.
		//
		//W systemach Linux można wykorzystać polecenie systemowe locale -a 
		//do sprawdzenia nazw obsługiwanych lokalizacji.
	}
	catch(const std::runtime_error& e)
	{
		//jeżeli dana lokalizacja nie jest obsługiwana konstruktor locale() rzuca wyjątek runtime_error
		cout<<e.what()<<endl;
		exit(1);
	}

	/*Obiekty klasy std::locale udostępniają usługi lokalizacyjne za pomocą obiektów zwanych aspektami (ang. facets).
	Biblioteka standardowa oferuje kilkanaście predefiniowanych aspektów, w tym pliku przedstawię ich
	wykorzystanie na przykładzie aspektów numeric_put, numeric_get, time_put, money_put, money_get oraz collate*/
	
	/*Wykorzystanie aspektu numeric_put - aspekt ten umożliwia wypisywanie do strumienia liczb oraz wartości
	binarnych w przyjętym przez daną lokalizację formacie.
	Najwygodniejszą metodą korzystania z aspektów numeric_put oraz numeric_get jest wykorzystanie
	operatorów wejścia, wyjścia.
	Pozostałe aspekty nie posiadają tych operatorów co powoduje konieczność posługiwania się specjalnym
	szablonem use_facet<>.*/
	bool bval = true;
	long lval = 644626L;
	unsigned long ulval = 23425334UL;
	double dval = 364657.6274; 
	long double ldval = 127384212.7573;
	cout.precision(15);

	for(LocVectorIter it = locVector.begin(); it!=locVector.end(); ++it)
	{
		//wywołanie metody imbue strumienia powoduje ustawienie go w tryb pracy z określona w parametrze lokalizacją
		cout.imbue(*it);
		
		//Metoda name klasy locale pobiera nazwę lokalizacji
		cout<<"Lokalizacja: "<<it->name()<<endl;
		cout<<"Wartość bool: "<<bval<<endl;
		cout<<"Wartość long: "<<lval<<endl;
		cout<<"Wartość unsigned long: "<<ulval<<endl;
		cout<<"Wartość double: "<<dval<<endl;
		cout<<"Wartość long double: "<<ldval<<endl<<endl;	
	}
	cout<<endl;

	/*Wykorzystanie aspektu numeric_get - aspekt ten umożliwia odczyt ze strumienia liczb oraz wartości
	binarnych w przyjętym przez daną lokalizację formacie*/
	stringstream stringStream;
	stringStream.precision(15);
	cout.imbue(defaultLoc);
	stringStream.imbue(defaultLoc);
	stringStream<<ldval;	//zapisujemy wartość long double do strumienia (aspekt numeric_put)
	ldval = 0;
	stringStream>>ldval;	//odczytujemy wartość long double ze strumienia (aspekt numeric_get)
	cout<<"numeric_get odczytał: "<<ldval<<endl<<endl;
	
	/*Przykład użycia aspektu time_put - aspekt ten umożliwia wypisywanie do strumienia daty i czasu
	w przyjętym przez daną lokalizację formacie*/
	time_t tt = time(NULL);
	struct tm* tm_buf = localtime(&tt);
	struct tm currentTime;
	memcpy(&currentTime,tm_buf,sizeof(struct tm));
	//struktura currentTime zawiera teraz aktualny czas lokalny

	for(LocVectorIter it = locVector.begin(); it!=locVector.end(); ++it)
	{
		cout.imbue(*it);
		cout<<"Lokalizacja "<<it->name()<<", data i czas: ";

		//używamy szablonu use_facet<> w celu skorzystania z aspektu time_put
		//parametry metody put aspektu time_put:
		//	- iterator wyjściowy strumienia
		//	- strumień wyjściowy
		//	- znak wypełnienia używany przy wyrównywaniu wartości w polu o większej szerokości
		//	- wskaźnik na strukturę tm
		//	- tryb wypisywania 'c' oznacza wypisanie pełnej daty i czasu
		use_facet<time_put<char,OstreamIter> >(*it).put(OstreamIter(cout),cout,cout.fill(),&currentTime,'c');
		cout<<endl;
	}
	/*
	W bibliotece standardowej zaimplementowano również aspekt time_get służący do odczytu ze strumienia
	czasu zapisanego w formacie danej lokalizacji, jednakże w dostępnych dla mnie implementacjach C++
	nie udało mi się go zastosować w sposób dający właściwy rezultat.
	*/
	cout<<endl;
	/*
	Przykład wykorzystania aspektu money_put - jest to formatowane wyjście dla wartości pienieżnych
	*/
	long double moneyval = 231623; 	//wartość pieniężna podawana jest w najmniejszej dla danej waluty jednostkach
					//np dla waluty polskiej wartość ta oznacza 2316.23 zł
					
	for(LocVectorIter it = locVector.begin(); it!=locVector.end(); ++it)
	{
		stringStream.clear();
		stringStream.str("");
		stringStream.imbue(*it);
		cout<<"Lokalizacja: "<<it->name()<<", format wartości pieniężnej: ";
		
		//parametry metody put aspektu money_put:
		//	- iterator wyjściowy strumienia
		//	- wartość boolean określająca czy wypisywać symbol waluty w formie lokalnej (false) czy międzynarodowej (true)
		//	- strumień wyjściowy
		//	- znak wypełnienia używany przy wyrównywaniu wartości w polu o większej szerokości
		//	- wartość pieniężna
		use_facet<money_put<char,OstreamIter> >(*it).put(OstreamIter(stringStream),false,stringStream,stringStream.fill(),moneyval);
		cout<<stringStream.str()<<endl;
	}
	cout<<endl;
	/*
	Przykład wykorzystania aspektu money_get - analizuje strumień w celu odczytania wartości pieniężnej w formacie danej lokalizacji
	*/			
	ios_base::iostate state;
	cout.imbue(locVector.back());
	//cout<<"Lokalizacja: "<<it->name()<<", format wartości pieniężnej: ";
		
	moneyval=0;
	//parametry metody get aspektu money_get:
	//	- iterator wejściowy strumienia wskazujący miejsce od którego nalezy rozpocząć wczytywanie
	//	- iterator wejściowy strumienia wskazujący miejsce przed którym nalezy zakończyć wczytywanie
	//	- wartość boolean określająca czy stosowany jest symbol waluty w formie lokalnej(false) czy międzynarodowej (true)
	//	- strumień wejściowy
	//	- referencja na stan strumienia - gdy odczyt wartości zakończy się niepowodzeniem ustawiany jest bit ios_base::failbit w zmiennej state
	//	- referencja na wartość pieniężną
	use_facet<money_get<char,IstreamIter> >(locVector.back()).get(IstreamIter(stringStream),IstreamIter(),false,stringStream,state,moneyval);
	cout<<"money_get odczytał "<<moneyval<<" z "<<stringStream.str()<<endl<<endl;

	
	string str1("ala");
	string str2("kot");
	cout.imbue(defaultLoc);

	/*Przykład użycia aspektu collate - aspekt ten umożliwia porównywanie alfabetyczne słów według przyjętej
	w danej lokalizacji kolejności liter alfabetu oraz udostępnia funkcję mieszającą.
	
	Metoda compare aspektu collate porównuje alfabetycznie (zgodnie z konwencją w danej lokalizacji)
	dwa ciągi znaków i zwraca:
	-1 - gdy pierwsze słowo poprzedza alfabetycznie drugie
	0 - gdy obie sekwencje są równe
	-1 - gdy drugie słowo poprzedza alfabetycznie pierwsze*/
	cout<<"compare: "<< use_facet<collate<char> >(defaultLoc).compare(str1.c_str(), str1.c_str()+str1.length(),
				str2.c_str(), str2.c_str()+str2.length()) <<endl;

	/*
	Metoda hash aspektu collate oblicza wartość funkcji mieszającej dla danego ciągu znaków
	*/
	cout<<"hash: "<< use_facet<collate<char> >(defaultLoc).hash(str1.c_str(), str1.c_str()+str1.length()) <<endl;
	
	return 0;
}
