/* 	hash_multimap.cpp
   	Created on: 2008-11-30
   	Author: Grzegorz Mroziewicz

	Program pokazuje przykładowe użycie kontenera   hash_multimap.
    
	Hash_multimap jest asocjacyjnym kontenerem służym do  przechowywania par elementów < klucz , wartość >.
	Wartość klucza nie musi być unikalna. Kontener pozwala na przechowywanie wielu elementów o tym samym kluczu.

	Program przedtswaia użycie kontenera dla typów standardowo obsługiwanych przez funkcję haszującą   ( skyscrapersExample() ) oraz 
	dla obiektów klas użytkownika ( flowersExample() ).  Zaprezentowane przykłady skupiają sie wokół charakterystycznych dla kontenera cech,
	a więc działania na obiektach o identycznym kluczu.	*/

#include <iostream>
#include <boost/type.hpp>
#include <boost/functional.hpp>
#include <boost/functional/hash.hpp>
#include <cstdlib>
#include <ext/hash_map>

using namespace std;
using namespace __gnu_cxx; /* Używam implementacji g++ hash_multimap*/
using namespace boost;

class Kwiat{
public:
	Kwiat() : name( "" ), name_latin( "" ){};

	Kwiat( std::string nazwaPolska, std::string nazwaBotaniczna )
		:	name( nazwaPolska ),
			name_latin( nazwaBotaniczna ){};

	virtual ~Kwiat(){};
	
	const Kwiat& operator=(const Kwiat &a){
		name = a.name;
		name_latin = a.name_latin;
		return *this;
	}
	
	Kwiat(const Kwiat &a){
		*this = a;
	}
	/* zdefinioanie hashu dla klasy kwiat , przyjmujemy że nazwa łacińska jest unikalna*/
	friend std::size_t hash_value(Kwiat const&  x){
        boost::hash<std::string> hasher;
		std::size_t seed = hasher(x.name_latin);
        return seed;
    }

	std::string name;
	std::string name_latin;
	// inne dane
};
/* zdefiniowanie operatora dla klasy operującego na oboektach klasy Kwiat niezbędne dla wyznaczania "równosci" obiektów w kontenerze hash_multimap*/
struct hashKwiat{
	bool operator()(const Kwiat &k1, const Kwiat &k2) const{
		return k1.name_latin == k2.name_latin ;
	}
};

class Obserwacja{
public:
	Obserwacja( int numer ) :id(numer) {};
	~Obserwacja(){};
	const Obserwacja& operator=(const Obserwacja &a) { id=a.id; return *this; }
	Obserwacja(const Obserwacja &a){ *this = a; }
	int id;
	//tutaj znajdują się pozostale pola : parametry obserwacji
};

/* definicja typów map używanych w programie*/
typedef hash_multimap<std::string, std::string, boost::hash<std::string> > string_string_map;
typedef hash_multimap<Kwiat, Obserwacja, boost::hash<Kwiat>, hashKwiat > Kwiat_Obserwacja_map;


void skyscrapersExample(){

	string_string_map greatSkyscrapersMap;

	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("New York", "Chrysler Building"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Hong Kong", "Bank of China Tower"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Hong Kong","Two International Finance Center"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("New York", "Empire State Building"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Dubai","Burj Al Arab"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Kuala Lumpur", "Petronas Towers"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Hong Kong", "The Center"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Riyadh", "Kingdom Centre"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Shanghai", "Jin Mao Tower"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Chicago", "John Hancock Center"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("New York", "Woolworth Building"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Hong Kong","Highcliff"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Chicago", "Sears Tower"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Kuala Lumpur", "Menara Telekom"));
	greatSkyscrapersMap.insert(std::pair<std::string, std::string>("Dubai","Chelsea Tower"));
	/*
		Użycie iteratora do wypisanie wszystkich par < klucz , wartość > znajdujących się w mapie
	*/
	cout << endl << "Dane o wieżowcach zawarte w mapie : " << endl;
	cout << "\t< " << "MIASTO" << " , " << "NAZWA" << " >" << endl;
	for (string_string_map::iterator it = greatSkyscrapersMap.begin(); it != greatSkyscrapersMap.end(); ++it){
		cout << "\t< " << (*it).first << " , " << (*it).second << " >" << endl;
	}
	/*
		Użycie metody count do wyznaczenie liczby wszystkich elementów o zadanym kluczu
	*/
	std::string city1 = "New York";
	std::string city2 = "Warsaw";
	cout << endl << "Liczba wspaniałych wieżowców w mieście " << city2 << " : " << greatSkyscrapersMap.count( city2 ) << endl;
	cout << "Liczba wspaniałych wieżowców w mieście " << city1 << " : " << greatSkyscrapersMap.count( city1 ) << endl;
	/*
		Użycie metody  equal_range do wybrania  pary iteratorów wskazujących kolekcję elementów o zadanym kluczu	
	*/
	std::string selectedCity = "Hong Kong";
	cout << endl << "Lista wspaniałych wieżowców w mieście "<< selectedCity << endl;

	pair<string_string_map::iterator, string_string_map::iterator> skyscrapersInSelectedCity;
	skyscrapersInSelectedCity = greatSkyscrapersMap.equal_range( selectedCity );
    for ( string_string_map::iterator it2 = skyscrapersInSelectedCity.first; it2 != skyscrapersInSelectedCity.second; ++it2 ){
	   cout << "\t" << (*it2).second << endl;
    }
};

void flowersExample(){
	Kwiat_Obserwacja_map mapaProbekKwiatow;
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >(  Kwiat( "chaber bławatek" , "Centaurea cyanus" ) , Obserwacja( 1 ) ));
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >( Kwiat( "bniec czerwony" , "Silene dioica" ) , Obserwacja( 1 ) ));
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >( Kwiat( "moczarka kanadyjska" , "Elodea canadensis" ) , Obserwacja( 1 ) ));
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >( Kwiat( "moczarka kanadyjska" , "Elodea canadensis" ) , Obserwacja( 2 ) ));
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >( Kwiat( "niezapominajka leśna" , "Myosotis sylvatica" ) , Obserwacja( 1 ) ));
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >( Kwiat( "niezapominajka leśna" , "Myosotis sylvatica" ) , Obserwacja( 2 ) ));
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >( Kwiat( "niezapominajka leśna" , "Myosotis sylvatica" ) , Obserwacja( 3 ) ));
	mapaProbekKwiatow.insert( std::pair< Kwiat , Obserwacja >( Kwiat( "czyściec kosmaty" , "Stachys germanica" ) , Obserwacja( 1 ) ));
	
	cout << endl << "Mapa kwiatów : " << endl;
	cout << "\t< " << "KWIAT" << " , " << "OBSERWACJA" << " >" << endl;
	for (Kwiat_Obserwacja_map::iterator it = mapaProbekKwiatow.begin(); it != mapaProbekKwiatow.end(); ++it){
		cout << "\t< " << (*it).first.name_latin<< " , " << (*it).second.id << " >" << endl;
	}
}

int main(){
	skyscrapersExample();
	flowersExample();
	return 0;
}
