//============================================================================
// Name        : streambuffering.cpp
// Author      : Bartlomiej Gajewski
// Version     : 1.0
// Copyright   : Praca Domowa z ZPR PW
// Description : przykład działania strumienia z buforem.
//               Zaleca się równoległe śledzenie zawartości plików tworzonych
//               przez prezentacje
//============================================================================

#include <iostream>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>

using namespace std;

//Bardzo użyteczny szablon do c++owego konwertowania na string
template<class T>
inline std::string to_string(const T& t) {
	std::stringstream ss;
	ss << t;
	return ss.str();
}

int main();

int presentBasics() {
	//zdefiniowanie buforu jako tablicy znakow
	//rozmiar tablicy okresla pojemnosc bufora.
	char buffer[BUFSIZ];
	FILE *pFile1, *pFile2;
	char ok[3];
	int i = 0;

	pFile1 = fopen("buforowany.txt", "w");
	pFile2 = fopen("niebuforowany.txt", "w");
	cout << "Pliki utworzone" << endl;

	//ustawienie buforu na pliku 1
	//Bufor nie może być mniejszy niż BUFSIZ=512!!
	setbuf(pFile1, buffer);
	fputs("test1: strumien buforowany\n", pFile1);
	cout
			<< "Do strumienia zapisano tekst, plikiem pozostal nie zmieniony. Tekst znajduje sie w buforze:"
			<< endl << buffer << endl;
	cin >> ok;

	//oczyszczenie bufora
	fflush(pFile1);
	cout << "Wywolano fflush, zawartosc bufora zapisana do pliku buforowanego"
			<< endl;
	cin >> ok;

	//ustawienie zerowego buforu na pliku2
	setbuf(pFile2, NULL );
	fputs("test2: strumien nie buforowany\n", pFile2);
	cout
			<< "Wstawiono tekst do strumienia. Plik jest nadpisywany co znak, na bieząco (nie potrzebny fflush)"
			<< endl;
	cin >> ok;

	//przepelnienie bufora. Bufor ma zdefiniowana pojemnosc 512 ciagow!
	//proba zapisu do pliku 520 znakow:
	char content[2];
	for (i = 1; i < 521; i++) {
		strcpy(content, to_string(i % 10).c_str());
		fputs(content, pFile1);
	}
	cout << "Przepelniono bufor, znaków zapisanych do pliku: " << BUFSIZ
			<< endl;
	cout << "zawartosc bufora:" << buffer << endl;
	//w buforze znajduje sie teraz reszta znakow, czyli ostatnie 8
	//uwaga: to strumien przechowuje numer znaku konca buforu do zapisu!
	cin >> ok;

	//zapisanie pozostalych 8 znakow.
	fflush(pFile1);
	cout << "Wpisanie do strumienia pozostalej zawartosci bufora" << endl;
	cin >> ok;

	fputs("kolejne dane; ", pFile1);
	cout << "wstawienie kolejnych danych do strumienia. Zawartość bufora: "
			<< endl << buffer << endl;
	strcpy(buffer, "\nmodyfikacja;");
	cout << "bezposrednia modyfikacja bufora. Obecna zawartość bufora: "
			<< buffer << endl;
	cin >> ok;
	fflush(pFile1);

	//zamnięcie strumieni, zapis zawartosci buforów do strumieni (o ile bufor byl zainicjalizowany)
	fputs("\nkoniec buforowanego", pFile1);
	fclose(pFile1);
	fputs("\nkoniec niebuforowanego", pFile2);
	fclose(pFile2);
	cout
			<< "Zamknieto strumienie, zawartosc buforow zapisana do strumieni (plików) "
			<< endl;
	cin >> ok;

	cout << "Na koniec niekontrolowane wyjście:" << endl;
	//ponowna inicjalizacja pliku
	pFile1 = fopen("buforowany.txt", "a");
	strcpy(buffer, "");
	setbuf(pFile1, buffer);
	fputs("w przypadku awarii ten tekst nie zostanie zapisany!", pFile1);
	cout << "Zawartosc buforu: \n" << buffer << endl;
	cout << "wpisz 'ok' aby zakończyć prezentację wyjątkiem" << endl;
	cin >> ok;
	if (strcmp(ok, "ok") == 0) {
		throw "Koniec programu, ostatnia zawartość niezapisana!";
	} else {
		cout
				<< "Wpisales co innego, strumien zamknięty w kontrolowany sposób, zawartosc bufora zapisana"
				<< endl;

	}
	cout << "koniec prezentacji podstaw" << endl;
	main();
	return 0;
}

//prezentuje dzialanie Streambuf
int presentStreambuf() {
	char ok[3];
	char mybuffer[512];

	//deklaracja strumienia
	fstream filestr;
	//przypisanie buforu do strumienia
	filestr.rdbuf()->pubsetbuf(mybuffer, 512);

	streambuf *psbuf;
	streambuf *coutsave;

	filestr.open("strambuf.txt", ios_base::out);
	filestr << "Test streambuf\n";
	cout << "Zawartosc jeszcze nie zapisana." << endl;
	cout << "Zawartosc buforu:" << mybuffer << endl;
	cin >> ok;

	cout << "Teraz nastapi zapis do pliku!";
	cin >> ok;
	filestr.flush();

	cout << "Zawartosc zapisana do pliku" << endl;
	cin >> ok;
	cout << "Teraz nastapi przepelnienie buforu:" << endl;

	for (int i = 1; i < 521; i++) {
		filestr << to_string(i % 10);
	}
	cout << "Pozostala zawartosc buforu:" << mybuffer << endl;
	cin >> ok;
	cout << "Nastapi wpisanie do strumienia pozostalej zawartosci bufora"
			<< endl;
	filestr.flush();
	cin >> ok;
	cout << "Do pliku zostalo wstawione 520 znaków" << endl;

	//Przekazanie buforu z jednego strumienia na drugi:
	cout << "UWAGA: nastapi podmiana buforu standartowego wyjscia!" << endl;
	cin >> ok;
	psbuf = filestr.rdbuf();
	coutsave = cout.rdbuf();
	cout.rdbuf(psbuf);
	cout << "\nTo zostanie zapisane do pliku!";
	filestr << "\nTo takze zostanie zapisane do pliku!";
	filestr.close();

	cout.rdbuf(coutsave);
	cout << "koniec prezentacji streambuf" << endl;
	cin >> ok;
	main();
	return 0;
}

int main() {
	char ok[3] = "";
	cout << endl << "Prezentacja buforowania." << endl;
	cout << "Zaleca się równoległe śledzenie zawartości plików tworzonych "
			<< endl;
	cout << "przez prezentacje. Podczas prezentacji nalezy wpisywac znaki z"
			<< endl;
	cout << "klawiatury, by przejsc dalej (lub wpisac 'ok')" << endl;
	cout << "Wpisz:" << endl;
	cout << "  '1' aby pokazać dzialania na plikach" << endl;
	cout << "  '2' aby pokazać dzialania na streambuf" << endl;
	cout << "  co innego aby wyjść" << endl;
	cin >> ok;
	if (strcmp(ok, "1") == 0) {
		presentBasics();
	} else if (strcmp(ok, "2") == 0) {
		presentStreambuf();
	} else {
		cout << "Koniec; " << endl;
		return 0;
	}
}

