
#include <iostream>

/* Poznamka:
 * v kodu vetsinou chybely osetreni pripadu, kdy `s` je prazdny string, napr. v
 * length(). Vetsinu chyb (kterych jsem si vsimnul) jsem doopravil. Pouzivejte
 * s rozvahou, muzou nastat problemy.
 */

class String {
  char *s;

  void assign(const char *str) {
    if (!str)
      goto empty;

    size_t length;
    for (length = 0; str[length]; length++)
      ;
    if (!length)
      goto empty;

    s = new char[length + 1];
    for (size_t i = 0; i <= length; i++) {
      s[i] = str[i];
    }
    return;

  empty:
    s = nullptr;
  }

public:
  /* konstruktory, destruktory a prirazeni */
  String() : s(nullptr) {
    std::cerr << "Prazdny string @" << this << std::endl;
  }

  String(const String &x) {
    assign(x.s);
    std::cerr << "Kopirovanie @" << this << std::endl;
  }

  String(const char *str) {
    assign(str);
    std::cerr << "Kopie z bufferu @" << this << std::endl;
  }

  ~String() {
    clear();
    std::cerr << "Destrukcia @" << this << std::endl;
  }

  void operator=(const String &second) {
    clear();
    assign(second.c_str());
    std::cerr << "Prirazeni @" << this << std::endl;
  }

  /* jednoduche pomocne funkce */
  void clear() {
    if (empty())
      return;
    delete[] s;
    s = nullptr;
  }

  const char *c_str() const { return s; }

  bool empty() const { return !s; }

  char &operator[](size_t i) { return s[i]; }

  /* hodi se -- do indexovanych prvku v konstantnim
   * vektoru by nemelo jit zapisovat */
  const char &operator[](size_t i) const { return s[i]; }

  size_t length() const {
    if (!s)
      return 0;
    size_t len = 0;
    while (s[len])
      ++len;
    return len;
  }

  /* spojovani stringu */
  void operator+=(const String &second) {
    std::cerr << "Pricitani += @" << this << std::endl;
    size_t len1 = length();
    size_t len2 = second.length();
    if (!len1 && !len2)
      return;
    char *s2 = new char[len1 + len2 + 1];
    for (size_t i = 0; i < len1; i++)
      s2[i] = s[i];
    for (size_t i = 0; i < len2; i++)
      s2[i + len1] = second.s[i];
    s2[len1 + len2] = 0;
    clear();
    s = s2;
  }

  String operator+(const String &second) const {
    String temp(*this);
    temp += second;
    return temp;
  }

  /* porovnavani */
  bool operator==(const String &other) {
    std::cerr << "Porovnani == @" << this << std::endl;
    char *p = s, *q = other.s;
    if (!(p && q))
      return p == q;
    for (; *p && *q; p++, q++)
      if (*p != *q)
        return false;
    if (*p != *q)
      return false;
    return true;
  }

  bool operator<(const String &other) {
    std::cerr << "Porovnani < @" << this << std::endl;
    char *p = s, *q = other.s;
    if (!(p && q))
      return p < q;
    for (; *p && *q; p++, q++) {
      if (*p == *q)
        continue;
      if (*p > *q)
        return false;
      return true;
    }
    return *p < *q;
  }
};

String reverse(const String &str) {
  String r = String(str.c_str());
  size_t len = r.length();
  for (size_t i = 0; i < len / 2; i++) {
    std::swap(r[i], r[len - i - 1]);
  }
  return r;
}

std::ostream &operator<<(std::ostream &os, const String &s) {
  if (!s.empty())
    os << s.c_str();
  return os;
}

#include <algorithm> //std::sort
#include <vector> //std::vector

int main() {
  String a("test");
  String b("aaa");
  std::cerr << " *** Pricitani" << std::endl;
  a += b;
  std::cerr << " *** Otoceni" << std::endl;
  b = reverse(a);
  std::cerr << " *** Slepovani a vypsani" << std::endl;
  std::cout << (reverse(b) + b + "aaa") << std::endl;

  // demo -- string se chova pomerne rozumne i kdyz ho pouzivate v ramci jinych
  // kontejneru. Je pekne pozorovat kolik konstrukci a destrukci vlastne
  // probehne.
  std::cerr << " *** Vyrabeni vektoru" << std::endl;
  std::vector<String> v;
  v.push_back("test");
  v.push_back("test2");
  v.push_back("asd");
  v.emplace_back("qwe"); // emplace nevytvori "mezihodnotu", takze je trochu
                         // uspornejsi (v logu bude jen jedna kopie z bufferu
                         // primo na misto kde string ma vzniknout)
  v.emplace_back(); // prazdny string
  v.push_back("zxc"); // realokace vektoru pri pushi je tady uz
                      // pomerne narocna operace :]

  std::cerr << " *** Kopie vektoru" << std::endl;
  std::vector<String> v2 = v; // tohle vyrobi celkem dost kopii

  std::cerr << " *** Setrideni vektoru" << std::endl;
  std::sort(v2.begin(), v2.end());
  for (auto &i : v2)
    std::cout << "> " << i << std::endl;

  std::cerr << " *** Vycisteni jednoho vektoru" << std::endl;
  v.clear();

  std::cerr << " *** Konec" << std::endl;
  return 0;
}
