#include "Datei.h"
#include "Text.h"
#include <direct.h>
#include <Shlwapi.h>

#pragma comment(lib, "Shlwapi.lib")

using namespace Framework;

// Inhalt der Datei Klasse aus Datei.h
// Konstruktor 
Datei::Datei()
{
	ref = 1;
	stream = 0;
	pfad = 0;
	gr = 0;
}

// Destruktor 
Datei::~Datei()
{
	if( stream )
		delete stream;
	if( pfad )
		pfad->release();
}

// nicht constant 
void Datei::setDatei( const char *pfad ) // setzt die Datei
{
	if( istOffen() )
		schließen();
	if( !this->pfad )
		this->pfad = new Text();
	this->pfad->setText( pfad );
	gr = 0;
}

void Datei::setDatei( Text *pfad )
{
	if( istOffen() )
		schließen();
	if( !this->pfad )
		this->pfad = new Text();
	this->pfad->setText( pfad );
	gr = 0;
}

bool Datei::umbenennen( const char *pfad ) // benennt die Datei um und verschiebt sie eventuell
{
	if( !pfad )
		return 0;
	if( DateiUmbenennen( this->pfad->getThis(), new Text( pfad ) ) )
	{
		this->pfad->setText( pfad );
		return 1;
	}
	return 0;
}

bool Datei::umbenennen( Text *pfad )
{
	if( !this->pfad )
	{
		pfad->release();
		return 0;
	}
	if( DateiUmbenennen( this->pfad->getThis(), pfad->getThis() ) )
	{
		this->pfad->setText( pfad->getText() );
		return 1;
	}
	return 0;
}

bool Datei::löschen() // löscht die Datei
{
	if( !pfad )
		return 0;
	gr = 0;
	return DateiLöschen( pfad->getThis() );
}

bool Datei::erstellen() // erstellt die Datei
{
	if( !pfad )
		return 0;
	return DateiPfadErstellen( pfad->getThis() );
}

bool Datei::öffnen( int style ) // öffnet die Datei
{
	if( !pfad )
		return 0;
	if( stream )
		delete stream;
	stream = new std::fstream();
	std::ios_base::openmode om = std::ios::binary;
	if( ( style | D_Style::lesen ) == style )
		om |= std::ios::in;
	if( ( style | D_Style::schreiben ) == style )
		om |= std::ios::out;
	stream->open( pfad->getText(), om );
	if( ( style | D_Style::ende ) == style )
	{
		if( ( style | D_Style::lesen ) == style ) 
			stream->seekg( 0, std::ios::end );
		if( ( style | D_Style::schreiben ) == style ) 
			stream->seekp( 0, std::ios::end );
	}
	if( !stream->is_open() || !stream->good() )
	{
		delete stream;
		stream = 0;
		return 0;
	}
	return 1;
}

void Datei::setLPosition( __int64 pos, bool ende ) // setzt die Leseposition
{
	if( !pfad )
		return;
	if( stream )
	{
		if( ende )
			stream->seekg( pos, std::ios::end );
		else
			stream->seekg( pos, std::ios::beg );
	}
}

void Datei::setSPosition( __int64 pos, bool ende ) // setzt die Schreibeposition
{
	if( !pfad )
		return;
	if( stream )
	{
		if( ende )
			stream->seekp( pos, std::ios::end );
		else
			stream->seekp( pos, std::ios::beg );
	}
}

void Datei::schreibe( char *bytes, int län ) // schreibt bytes in datei
{
	if( !pfad )
		return;
	if( stream )
		stream->write( bytes, län );
}

void Datei::lese( char *bytes, int län ) // ließt bytes aus datei
{
	if( !pfad )
		return;
	if( stream )
		stream->read( bytes, län );
}

Text *Datei::leseZeile() // ließt eine zeile
{
	if( !pfad | !stream )
		return 0;
	if( istEnde() || !stream->good() )
		return 0;
	Text *ret = new Text( "" );
	__int64 län = getGröße();
	for( char c = 0; c != '\n' && stream->tellg() < län; )
	{
		stream->read( &c, 1 );
		if( c )
			ret->anhängen( (const char*)&c, 1 );
	}
	return ret;
}

void Datei::schließen() // schließt die Datei
{
	if( !pfad )
		return;
	if( stream )
		stream->close();
	delete stream;
	stream = 0;
}

// constant 
bool Datei::istOrdner() const // prüft, ob die Datei ein Ordner ist
{
	if( !pfad )
		return 0;
	return DateiIstVerzeichnis( pfad->getThis() );
}

bool Datei::istOffen() const // prüft, ob die Datei geöffnet ist
{
	if( !pfad )
		return 0;
	if( stream )
		return stream->is_open() && stream->good();
	return 0;
}

int Datei::getUnterdateiAnzahl() const // gibt die Anzahl der unterdateien an
{
	if( !pfad )
		return 0;
	if( !DateiIstVerzeichnis( pfad->getThis() ) )
		return 0;
	int ret = 0;
	HANDLE fHandle;
	WIN32_FIND_DATA wfd;
	Text *stxt = new Text( pfad->getText() );
	stxt->ersetzen( '/', '\\' );
	if( stxt->positionVon( '\\' ) == stxt->getLänge() - 1 )
		stxt->anhängen( "*" );
	else
		stxt->anhängen( "\\*" );
	fHandle=FindFirstFile( stxt->getText(), &wfd );
	stxt->release();
	FindNextFile( fHandle, &wfd );
	while( FindNextFile( fHandle, &wfd ) )
		ret++;
	FindClose( fHandle );
	return ret;
}

TArray< Text > *Datei::getDateiListe() const // gibt eine Liste mit unterdateien zurück
{
	if( !pfad )
		return 0;
	if( !DateiIstVerzeichnis( pfad->getThis() ) )
		return 0;
	HANDLE fHandle;
	WIN32_FIND_DATA wfd;
	Text *stxt = new Text( pfad->getText() );
	stxt->ersetzen( '/', '\\' );
	if( stxt->positionVon( '\\' ) == stxt->getLänge() - 1 )
		stxt->anhängen( "*" );
	else
		stxt->anhängen( "\\*" );
	fHandle=FindFirstFile( stxt->getText(), &wfd );
	stxt->release();
	FindNextFile( fHandle, &wfd );
	TArray< Text > *ret = new TArray< Text >();
	int count = 0;
	while( FindNextFile( fHandle, &wfd ) )
	{
		Text *txt = new Text( wfd.cFileName );
		ret->add( txt, count, 0 );
		count++;
	}
	FindClose( fHandle );
	return ret;
}

__int64 Datei::getGröße() const // gibt die Größe der Datei zurück
{
	if( !pfad )
		return 0;
	if( gr )
		return gr;
	if( !stream || !istOffen() )
	{
		std::fstream *stream = new std::fstream();
		stream->open( pfad->getText(), std::ios::binary | std::ios::in );
		__int64 tmp = stream->tellg();
		stream->seekg( 0, std::ios::end );
		__int64 ret = stream->tellg();
		stream->seekg( tmp, std::ios::beg );
		stream->close();
		delete stream;
		__int64 *größe = (__int64*)&gr;
		*größe = ret;
		return ret;
	}
	__int64 tmp = stream->tellg();
	stream->seekg( 0, std::ios::end );
	__int64 ret = stream->tellg();
	stream->seekg( tmp, std::ios::beg );
	__int64 *größe = (__int64*)&gr;
	*größe = ret;
	return ret;
}

bool Datei::existiert() const // prüft, ob die Datei existiert
{
	if( !pfad )
		return 0;
	return DateiExistiert( pfad->getThis() );
}

__int64 Datei::getLPosition() const // gibt die Leseposition zurück
{
	if( !stream )
		return 0;
	return stream->tellg();
}

__int64 Datei::getSPosition() const // gibt die Schreibeposition zurück
{
	if( !stream )
		return 0;
	return stream->tellp();
}

bool Datei::istEnde() const // prüft, ob die Datei zu ende ist
{
	if( !stream )
		return 1;
	__int64 i = getGröße();
	return stream->tellg() >= i;
}

Text *Datei::getPfad() const // gibt den Dateipfad zurück
{
	return pfad ? pfad->getThis() : 0;
}

Text *Datei::zPfad() const
{
	return pfad;
}

// Reference Counting 
Datei *Datei::getThis()
{
	ref++;
	return this;
}

Datei *Datei::release()
{
	ref--;
	if( !ref )
		delete this;
	return 0;
}

// Datei Funktionen
bool Framework::DateiPfadErstellen( Text* pfad ) // Erstellt eine Datei in dem Pfad
{
	pfad->ersetzen( "//", "\\" ); // Pfadangaben korrigieren
	pfad->ersetzen( "/", "\\" );
	for( int i = 0; i < pfad->anzahlVon( "\\" ); i++ ) // Jeden ordner erstellen wenn er nicht existiert
	{
		Text *t = pfad->getTeilText( 0, pfad->positionVon( "\\", i ) );
		if( !DateiExistiert( t->getThis() ) )
			_mkdir( t->getText() );
		t->release();
	}
	std::ofstream f( pfad->getText(), std::ios::binary ); // Datei erstellen
	f.close();
	return DateiExistiert( pfad );
}

bool Framework::DateiLöschen( Text *pfad ) // Löscht die angegebene Datei
{
	pfad->ersetzen( '\\', '/' );
	bool ret = 0;
	if( pfad )
	{ // prüfen ob Datei existiert
		if( !DateiIstVerzeichnis( pfad->getThis() ) )
			ret = DeleteFile( pfad->getText() ) == 1; // Datei löschen
		else
		{
			ret = 1;
			Datei *dat = new Datei();
			dat->setDatei( pfad->getThis() );
			int anz = dat->getUnterdateiAnzahl();
			TArray< Text > *liste = dat->getDateiListe();
			for( int i = 0; i < anz; i++ )
			{
				Text *pf = new Text( pfad->getText() );
				if( pf->getText()[ pf->getLänge() - 1 ] != '/' )
					pf->anhängen( "/" );
				pf->anhängen( liste->get( i, 0 ) );
				if( ret )
					ret = DateiLöschen( pf );
				else
					DateiLöschen( pf );
			}
			liste->release();
			if( ret )
				ret = RemoveDirectory( pfad->getText() ) == 1;
			else
				RemoveDirectory( pfad->getText() );
			dat->release();
		}
		pfad->release();
	}
	return ret;
}

bool Framework::DateiUmbenennen( Text *pfad_alt, Text *pfad_neu ) // Benennt die Datei um
{
	if( pfad_alt && pfad_neu )
	{
		bool ret = 0;
		if( DateiExistiert( pfad_alt->getThis() ) && !DateiExistiert( pfad_neu->getThis() ) ) // prüfen ob Dateien existieren
			ret = MoveFile( pfad_alt->getText(), pfad_neu->getText() ) == 1; // Datei umbenennen
		pfad_alt->release();
		pfad_neu->release();
		return ret;
	}
	return 0;
}

bool Framework::DateiExistiert( Text *pfad ) // Prüft, ob Datei existiert
{
	bool ret = PathFileExists( pfad->getText() ) != 0;
	pfad->release();
	return ret;
}

bool Framework::DateiIstVerzeichnis( Text *pfad ) // prüft, ob pfad ein Verzeichnis ist
{
	WIN32_FIND_DATA wfd;
	HANDLE handle = FindFirstFile( pfad->getText(), &wfd );
	pfad->release();
	if( handle == INVALID_HANDLE_VALUE )
		return 0;
	FindClose( handle );
	return ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0;
}