// Lösungen zu "Richard Kaiser: C++ mit dem Borland C++Builder 2007", ISBN: 978-3-540-69575-2
// Copyright Richard Kaiser, 2007. http://www.rkaiser.de. All rights reserved.
// Diese Quelltexte dürfen in eigenen Programmen verwendet werden, wenn sie einen Hinweis auf die Quelle (die ersten beiden Zeilen) enthalten. In keinem Fall wird eine Haftung für direkte, indirekte, zufällige oder Folgeschäden übernommen, die sich aus der Nutzung ergeben.
// It is permitted to use this source text in programs of your own, provided that it contains a reference to the source (the first two lines above). All liabilities for use of the code are disclaimed.

#include <vcl\vcl.h>
#pragma hdrstop
#include "FilesU.h"
#pragma resource "*.dfm"
TForm1 *Form1;

//---------------------------------------------------------------------------

#include "..\..\CppUtils\TestUtils.h"
#include <string>
using std::string;
const string TestDir="c:\\test";
const string TestTextfile=TestDir+"\\test.txt";
const string TestExefile= TestDir+"\\test.exe";

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
if (CreateDirectory(TestDir.c_str(),0))
  ShowMessage(AnsiString("directory '")+TestDir.c_str()+"' erzeugt");

// Damit die Dateien für die folgenden Aufgaben vorhanden sind:
if (!FileExists(TestTextfile.c_str()))
  {
    BOOL b=CopyFile("\\Loesungen_BCB2006\\Kap_4\\4.3\\FilesU.cpp",
        TestTextfile.c_str(), true);
    if (b) ShowMessage(TestTextfile.c_str()+AnsiString(" erzeugt"));
  }
if (!FileExists(TestExefile.c_str()))
  {
    BOOL b=CopyFile("\\Loesungen_BCB2006\\Kap_4\\4.3\\Debug_Build\\Files.exe",
        TestExefile.c_str(), true);
    if (b) ShowMessage(TestExefile.c_str()+AnsiString(" erzeugt"));
  }
// Die Windows-API-Funktion 'CopyFile' ist in der Online-Hilfe
// zum Win32-SDK beschrieben.

#ifdef _DEBUG
bool test_All(); // Prototyp
initTestUtils();
testUtilsSummary(Memo1,test_All());
#endif
}


// --------------- Aufgaben 4.3.3 --------------------------

// --------------- Aufgabe 1 a) ----------------------------

#include <fstream>
// using namespace std;

int countBytes(const char* fn)
{
char c;
int n=0;
ifstream f(fn,ios::binary);
if (!f) ShowMessage("open error");
while (f.read(&c,sizeof(c)))
  n++;
if (!f.eof()) ShowMessage("read error");
f.close();
return n;
}

int countBytesTxt(const char* fn)
{
char c;
int n=0;
ifstream f(fn);
if (!f) ShowMessage("open error");
while (f.read(&c,sizeof(c)))
  n++;
if (!f.eof()) ShowMessage("read error");
f.close();
return n;
}

int countBytes(const char* fn, bool binary)
{
char c;
int n=0;
ifstream f;
if (binary) f.open(fn,ios::binary);
else f.open(fn);
// wie
// ifstream f(fn,ios::binary); oder
// ifstream f(fn); // ohne ios::binary ist das Ergebnis sowohl für
                   // Text- als auch für Binärdateien falsch
if (!f) ShowMessage("open error");
while (f.read(&c,sizeof(c)))
  n++;
if (!f.eof()) ShowMessage("read error");
f.close();
return n;
}

void createTestfile(char* fn,int n)
{
ofstream fout((TestDir+fn).c_str());
char a[]={'1','2','3',26,'5','6'};
fout.write(a,n);
fout.close();
}

bool test_countBytes()
{
bool result=true;
createTestfile("0",0);
createTestfile("1",1);
createTestfile("6",6);

int n0=countBytes((TestDir+"0").c_str(),false);
if (!assertEqual(n0,0,"countBytes 0")) result=false;
int n0b=countBytes((TestDir+"0").c_str(),true);
if (!assertEqual(n0b,0,"countBytes 0 b")) result=false;

int n1=countBytes((TestDir+"1").c_str(),false);
if (!assertEqual(n1,1,"countBytes 1")) result=false;
int n1b=countBytes((TestDir+"1").c_str(),true);
if (!assertEqual(n1b,1,"countBytes 1 b")) result=false;

int n2=countBytes((TestDir+"6").c_str(),false);
if (!assertEqual(n2,3,"countBytes 1")) result=false;
int n2b=countBytes((TestDir+"6").c_str(),true);
if (!assertEqual(n2b,6,"countBytes 1 b")) result=false;

return result;
}

void __fastcall TForm1::CountBytesClick(TObject *Sender)
{
int n=countBytes(TestTextfile.c_str());
Memo1->Lines->Add(TestTextfile.c_str()+AnsiString(" (binary mode): ")+IntToStr(n)+" bytes");
n=countBytesTxt(TestTextfile.c_str());
Memo1->Lines->Add(TestTextfile.c_str()+AnsiString(" (text mode): ")+IntToStr(n)+" bytes");

n=countBytes(TestExefile.c_str());
Memo1->Lines->Add(TestExefile.c_str()+AnsiString(" (binary mode): ")+IntToStr(n)+" bytes");
n=countBytesTxt(TestExefile.c_str());
Memo1->Lines->Add(TestExefile.c_str()+AnsiString(" (text mode): ")+IntToStr(n)+" bytes");


n=countBytes(TestTextfile.c_str(),true);
Memo1->Lines->Add(TestTextfile.c_str()+AnsiString(" (binary): ")+IntToStr(n)+" bytes");
n=countBytes(TestTextfile.c_str(),false);
Memo1->Lines->Add(TestTextfile.c_str()+AnsiString(": ")+IntToStr(n)+" bytes");

n=countBytes(TestExefile.c_str(),true);
Memo1->Lines->Add(TestExefile.c_str()+AnsiString(" (binary): ")+IntToStr(n)+" bytes");
n=countBytes(TestExefile.c_str(),false);
Memo1->Lines->Add(TestExefile.c_str()+AnsiString(": ")+IntToStr(n)+" bytes");
// Die Ergebnisse stimmen nur für Dateien, die im Binärmodus geöffnet
// wurden.
}


// --------------- Aufgabe 1 b) ----------------------------

void CopyFile(const char* src, const char* dst)
{
ifstream fin(src,ios::binary);
AnsiString msg="open error ";
if (!fin) ShowMessage(msg+src);
ofstream fout(dst,ios::binary);
if (!fout) ShowMessage(msg+dst);
char c;
while (fin.read(&c,sizeof(c)))
  {
    fout.write(&c,sizeof(c));
    if (!fout) ShowMessage("write error");
  }
if (!fin.eof()) ShowMessage("read error");
fin.close();
fout.close();
}

const string CopyTextDest=TestDir+"\\Ctest.txt";
const string CopyExeDest =TestDir+"\\Ctest.exe";

void test_CopyFile()
{
CopyFile(TestTextfile.c_str(), CopyTextDest.c_str());
CopyFile(TestExefile.c_str(),  CopyExeDest.c_str());
}

void __fastcall TForm1::CopyFilesClick(TObject *Sender)
{
test_CopyFile();
}


// --------------- Aufgabe 1 c) ----------------------------

bool CompareFiles(const char* fn1, const char* fn2)
{
AnsiString msg="open error ";

ifstream f1(fn1,ios::binary);
if (!f1) ShowMessage(msg+fn1);

ifstream f2(fn2,ios::binary);
if (!f2) ShowMessage(msg+fn2);

bool gleich=true; // zwei leere Dateien sind gleich
while (f1 && f2 && gleich)
  {
    char c1,c2;
    f1.read(&c1,sizeof(c1));
    f2.read(&c2,sizeof(c2));

    gleich=(c1==c2);
  }
if (!f1 && !f1.eof()) ShowMessage("read error f1");
if (!f2 && !f2.eof()) ShowMessage("read error f2");

if (gleich)
  gleich=(f1.eof()==f2.eof()); // Falls die Dateien verschieden lang sind
f1.close();
f2.close();
return gleich;
/*
Die folgende Schleife funktioniert nicht, da wegen der short circuit
evaluation von booleschen Ausdrücken beim Ende von f1 in f2 nicht mehr
gelesen wird:

while (f1.read(&c1,sizeof(c1)) && f2.read(&c2,sizeof(c2)) && gleich)
  gleich=(c1==c2);
*/
}


bool test_copy_and_compare_files()
{
test_CopyFile();
bool result=true;
bool b=CompareFiles(TestTextfile.c_str(), CopyTextDest.c_str());
if (!assertEqual(b,true,"CompareFiles 1")) result=false;
b=CompareFiles(TestExefile.c_str(),  CopyExeDest.c_str());
if (!assertEqual(b,true,"CompareFiles 2"))  result=false;
b=CompareFiles(TestTextfile.c_str(), CopyExeDest.c_str());
if (!assertEqual(b,false,"CompareFiles 3"))  result=false;
b=CompareFiles(TestExefile.c_str(),  CopyTextDest.c_str());
if (!assertEqual(b,false,"CompareFiles 4")) result=false;
return result;
}

bool test_All()
{
bool result=true;
if (!test_countBytes()) result=false;
if (!test_copy_and_compare_files()) result=false;
return result;
}

void __fastcall TForm1::ComparefilesClick(TObject *Sender)
{
if (test_copy_and_compare_files())
  Memo1->Lines->Add("All tests passed");
else
  Memo1->Lines->Add("Some tests failed");
}


// --------------- Aufgabe 1 d) ----------------------------

void WriteNTString(ofstream& f,char* s)
{ // schreibt alle Zeichen des nullterminierten Strings s
  // einschließlich des Nullterminators in den stream f
f.write(s,strlen(s)+1);
}

string ReadNTString(ifstream& f)
{
// Die globale Funktion getline der Standardbibliothek
// von C++ ist ähnlich aufgebaut und sollte dieser Funktion
// vorgezogen werden.
string s;
char c;
while (f.read(&c,1) && c!=0)
  s=s+c;
return s;
}

void __fastcall TForm1::WritNTSClick(TObject *Sender)
{
const char* fn=(TestDir+"\\nts.txt").c_str();
ofstream out(fn,ios::binary);
if (out)
  {
    WriteNTString(out,"123");
    WriteNTString(out,"der nächste String ist leer");
    WriteNTString(out,"");
    WriteNTString(out,"A");
  }
out.close();
ifstream in(fn,ios::binary);
if (in)
  {
    string s=ReadNTString(in);
    while (in)
      {
        Memo1->Lines->Add(s.c_str());
        s=ReadNTString(in);
      }
  }
in.close();
}


// --------------- Aufgabe 1 e) ----------------------------

void TextFilter(const char* src, const char* dst)
{
AnsiString msg="open error ";
ifstream in(src,ios::binary);
if (!in) ShowMessage(msg+src);
ofstream out(dst,ios::binary);
if (!out) ShowMessage(msg+dst);

char c;
while (in.read(&c,sizeof(c)))
  if ((' '<=c && c<='z') || c=='\n') // bis auf diese Anweisung identisch
                        // mit CopyFile
    if (!out.write(&c,sizeof(c)))
      ShowMessage("write error");
if (!in.eof()) ShowMessage("read error");
in.close();
out.close();
}

void __fastcall TForm1::FileFilterClick(TObject *Sender)
{

TextFilter(TestTextfile.c_str(),(TestDir+"\\filt_t.txt").c_str());
TextFilter(TestExefile.c_str(), (TestDir+"\\filt_e.txt").c_str());
TextFilter((TestDir+"\\test1.doc").c_str(),
           (TestDir+"\\filt_w.txt").c_str());
}



// --------------- Aufgaben 4.3.4 --------------------------

#include "3_4.cpp"

// --------------- Aufgabe 1 -------------------------------

void __fastcall TForm1::TextToHtmlClick(TObject *Sender)
{
OpenDialog1->Filter = "Text (*.txt)|*.txt";
OpenDialog1->InitialDir="\\Loesungen_2006\\Text\\";
if (OpenDialog1->Execute())
  {
    AnsiString fn=ExtractFileName(OpenDialog1->FileName);
    AnsiString html_fn=ChangeFileExt(fn,".html");
    TextToHTML(OpenDialog1->FileName.c_str(), (TestDir.c_str()+AnsiString("\\")+html_fn).c_str());
    AnsiString url_dir=TestDir.c_str()+AnsiString("\\");
    if ((url_dir.Length()>=2) && (url_dir[2]==':'))
      url_dir[2]='|';
    WideString url="file:///"+url_dir+html_fn;
    CppWebBrowser1->Navigate(url,0,0,0,0);
    Form1->Caption=html_fn;
  }
}


// --------------- Aufgabe 2 -------------------------------

void __fastcall TForm1::KBToHtmlClick(TObject *Sender)
{
OpenDialog1->InitialDir=TestDir.c_str();
OpenDialog1->Filter = "Daten (*.dat)|*.DAT";
AnsiString default_fn="kb.dat";
AnsiString html_fn="kb.html";
OpenDialog1->FileName=default_fn;
if (OpenDialog1->Execute())
  {
    KBToHTML(OpenDialog1->FileName.c_str(),
         (TestDir.c_str()+AnsiString("\\")+html_fn).c_str());
    AnsiString url_testdir=TestDir.c_str();
    if ((url_testdir.Length()>=2) && (url_testdir[2]==':'))
      url_testdir[2]='|';
    WideString url="file:///"+url_testdir+"\\"+html_fn;
    CppWebBrowser1->Navigate(url,0,0,0,0);
    Form1->Caption="Kontobewegungen";
  }
}
