//=============================================================================

#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;

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

const string filename("telefonbuch4.1.txt");

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

bool starts_with(const string& src, const string& pattern)
{
   if (src.length() < pattern.length())
   {
      return false;
   }
   return src.substr(0, pattern.length()) == pattern;
}

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

class entry
{
public:
   void input();
   void print() const;

   void load(ifstream& in, int& line);
   void write(ofstream& out) const;

private:
   using numbers = vector<string>;
   numbers numbers_;
};

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

class book
{
public:
   void new_entry();

   void print() const;
   void find_and_print() const;

   void load(const string& filenname);
   bool write(const string& filenname) const;

private:
   using entries = map<string, entry>;
   entries entries_;
};

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

void entry::input()
{
   for (string no;;)
   {
      cout << "> Nr.: ";
      getline(cin, no);
      if (no.empty()) break;
      numbers_.push_back(no);
   }
}

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

void entry::print() const
{
   for (const string& no : numbers_)
   {
      cout << '(' << no << ") ";
   }
}

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

void entry::load(ifstream& in, int& line)
{
   for (string no;;)
   {
      ++line;
      getline(in, no);
      // Bei s.empty() Schleife beenden, da eine Leerzeile 
      // das Ende der Telefon-Nummern anzeigt.
      if (in.fail() || no.empty()) break;
      numbers_.push_back(no);
   }
}

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

void entry::write(ofstream& out) const
{
   for (const string& no : numbers_)
   {
      out << no << '\n';
   }
   out << '\n';
}

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

void book::new_entry()
{
   cout << "Neuer Eintrag\n";

   string name;
   cout << "> Name: ";
   getline(cin, name);

   if (name.empty())
   {
      cout << "Abbruch 'neuer Eintrag', da Name leer\n";
      return;
   }
   if (entries_.count(name) > 0)
   {
      cout << "Abbruch 'neuer Eintrag', da Name schon vorhanden\n";
      return;
   }

   entry& e = entries_[name];
   e.input();
}

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

void book::print() const
{
   cout << entries_.size() << " Eintraege:\n";
   for (auto& [name, numbers] : entries_)
   {
      cout << "* " << name << " => ";
      numbers.print();
      cout << '\n';
   }
}

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

void book::find_and_print() const
{
   string pattern;
   cout << "Suchen nach: ";
   getline(cin, pattern);

   auto lower = entries_.lower_bound(pattern);
   auto eit = cend(entries_);

   if (lower == eit || !starts_with(lower->first, pattern))
   {
      cout << "\nKein Name im Telefonbuch beginnt mit \"" << pattern << "\".\n";
      return;
   }

   cout << "\nGefunden:\n";
   for (; lower != eit; ++lower)
   {
      if (!starts_with(lower->first, pattern)) break;
      cout << "* " << lower->first << " => ";
      lower->second.print();
      cout << '\n';
   }
}

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

void book::load(const string& filename)
{
   cout << "Lade Daten aus der Datei \"" << filename << "\"...\n";

   ifstream in(filename);
   if (in.fail())
   {
      cout << "- Probleme beim Oeffnen der Datei.\n- 0 Eintraege geladen.\n";
      return;
   }

   string name;
   int line = 0;
   do
   {
      ++line;
      getline(in, name);
      if (in.fail()) break;
      entry& e = entries_[name];
      e.load(in, line);
   } while (!in.fail());
   if (!in.eof())
   {
      cout << "- Problem beim Lesen der Zeile " << line << '\n';
   }

   cout << "- " << entries_.size() << " Eintrage geladen.\n";
}

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

bool book::write(const string& filename) const
{
   cout << "Sichere Daten in die Datei \"" << filename << "\"...\n";

   ofstream out(filename);
   if (out.fail())
   {
      cout << "- Probleme beim Oeffnen der Datei.\n";
      return false;
   }

   for (auto& [name, numbers] : entries_)
   {
      out << name << '\n';
      numbers.write(out);
      if (out.fail())
      {
         cout << "- Probleme beim Schreiben in die Datei.\n";
         return false;
      }
   }

   cout << "- fertig\n";
   return true;
}

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

int main()
{
   cout << "Aufg_14_03_Telefonbuch_4_1\n\n";

   book b;
   b.load(filename);

   for (string s;;)
   {
      cout << "\nBitte waehlen Sie eine Aktion: \n"
         << "- e : Programmende\n"
         << "- l : Alle Eintraege auflisten\n"
         << "- s : Nach Eintrag suchen\n"
         << "- n : Neuen Eintrag eingeben\n"
         << "> ";
      getline(cin, s);
      if (s.length() != 1) continue;
      cout << '\n';

      switch (s[0])
      {
      case 'e':
         if (b.write(filename))
         {
            cout << "\nAuf Wiedersehen\n";
            return 0;
         }
         break;
      case 'l':
         b.print();
         break;
      case 's':
         b.find_and_print();
         break;
      case 'n':
         b.new_entry();
         break;
      }
   }
}
