#include <fstream>
#include <algorithm>
#include <string>
#include "tbb/pipeline.h"
#include "tbb/concurrent_hash_map.h"

struct Fragment {
	static const size_t rozmiar = 10000;
	char bufor[rozmiar];
	size_t koniec;
};

typedef tbb::concurrent_hash_map<std::string, size_t, tbb::tbb_hash_compare<std::string> > LicznikSlow;

class FiltrLicznikaSlow : public tbb::filter {
	LicznikSlow& slowa;
public:
	FiltrLicznikaSlow(LicznikSlow& licznikSlow) : 
		slowa(licznikSlow), tbb::filter(tbb::filter::parallel)
	{}
	void* operator ()(void *fragment) {
		Fragment& f = *static_cast<Fragment *>(fragment);
		size_t i = 0, poczSlowa;
		bool slowo = false;
		while(i < f.koniec) {
			if (!slowo && isalpha(f.bufor[i])) {
				slowo = true;
				poczSlowa = i;
			} else if (slowo && !isalpha(f.bufor[i])) {
				slowo = false;
				std::string s(f.bufor + poczSlowa, i - poczSlowa);
				LicznikSlow::accessor a;
				slowa.insert(a, s);
				a->second += 1;
			}
			++i;
		}
		if (slowo) {
			std::string s(f.bufor + poczSlowa, i - poczSlowa - 1);
			LicznikSlow::accessor a;
			slowa.insert(a, s);
			a->second += 1;
		}
		return &f;
	}
};

class FiltrPoczatkowy : public tbb::filter {
public:
	static const int iloscFragmentow = 4;
private:
	static const size_t rozmiar = 1000;
	std::fstream& fin;
	char bufor[rozmiar];
	int n, m, pozycjaBufora;
	Fragment fragmenty[iloscFragmentow];
public:
	FiltrPoczatkowy(std::fstream& plik_we) : 
	  fin(plik_we), m(0), pozycjaBufora(0), tbb::filter(tbb::filter::serial)
	{}
	void* operator ()(void *) {
		Fragment& f = fragmenty[pozycjaBufora];
		pozycjaBufora = (pozycjaBufora + 1) % iloscFragmentow;
		if (!fin.good()) return NULL;
		if (m != 0)
			std::copy(bufor, bufor + m, f.bufor);
		fin.read(f.bufor + m, f.rozmiar - m);
		n = fin.gcount();
		if (n + m == f.rozmiar && !fin.eof()) {
			m = f.rozmiar - rozmiar;
			while (m < f.rozmiar && !isspace(f.bufor[m])) 
				++m;
			std::copy(f.bufor + m, f.bufor + f.rozmiar, bufor);
			f.koniec = m;
			m = f.rozmiar - m;
		} else {
			f.koniec = n + m;
		}
		return &f;
	}
};

class FiltrRot13 : public tbb::filter {
public:
	FiltrRot13() : tbb::filter(tbb::filter::parallel) {}
	void* operator ()(void *fragment) {
		Fragment& f = *static_cast<Fragment *>(fragment);
		for (size_t i = 0; i < f.koniec; ++i) {
			if (isupper(f.bufor[i]))
				f.bufor[i] = 'A' + (f.bufor[i] - 'A' + 13) % 26;
			else if (islower(f.bufor[i]))
				f.bufor[i] = 'a' + (f.bufor[i] - 'a' + 13) % 26;
		}
		return &f;
	}
};

class FiltrKoncowy : public tbb::filter {
	std::fstream& fout;
public:
	FiltrKoncowy(std::fstream& plik_wy) : 
	  fout(plik_wy), tbb::filter(tbb::filter::serial)
	{}
	void* operator ()(void *fragment) {
		Fragment& f = *static_cast<Fragment *>(fragment);
		fout.write(f.bufor, f.koniec);
		return NULL;
	}
};

