/* StringRingbufferModif.cpp  -- with c-strings */
#include <iostream>
#include <cstring>
using namespace std;


class StringRingbuffer {
  static const int len = 5;

  char *ca[len];                 // array of pointers to c-strings
  int inx;                       // input index
  int outx;                      // output index
  int entries;                   // number of current entries

  void deleteCa();
  void copyCa(const StringRingbuffer&);
public:
  StringRingbuffer();
  StringRingbuffer(const StringRingbuffer&);
  ~StringRingbuffer();
  const StringRingbuffer& operator=(const StringRingbuffer&);
  void reset();                  // empty buffer
  bool isEmpty() const;          // answer whether buffer is empty
  bool isFull() const;           // answer whether buffer is full
  bool in(const char arr[]);     // input string and answer if successful
  bool out(char arr[], int maxLength);  // copy string into arr with maxLength
                                        // chars. Answer true if a string was
                                        // available, else false
  void print() const;            // print state of ringbuffer
};




void StringRingbuffer::deleteCa() {
  for (int i = outx; i < outx + entries; ++i) {
    delete[] ca[i % len];
  }
}



void StringRingbuffer::copyCa(const StringRingbuffer& rb) {
  char *tca[len];
  for (int i = outx; i < outx + entries; ++i) {
    tca[i % len] = new char[(strlen(rb.ca[i % len]) + 1) * sizeof(char)];
    strcpy(tca[i % len], rb.ca[i % len]);
  }
  for (int i = outx; i < outx + entries; ++i) {
    ca[i % len] = tca[i % len];
  }
}



inline StringRingbuffer::StringRingbuffer() : inx(0), outx(0), entries(0) { }



inline StringRingbuffer::StringRingbuffer(const StringRingbuffer& rb)
  : inx(rb.inx), outx(rb.outx), entries(rb.entries)
{
    copyCa(rb);
}



inline StringRingbuffer::~StringRingbuffer() {
  deleteCa();
}



const StringRingbuffer& StringRingbuffer::operator=(const StringRingbuffer& right) {
  if (&right != this) {
    deleteCa();
    inx = right.inx;
    outx = right.outx;
    entries = right.entries;
    copyCa(right);
  }
  return *this;
}



void StringRingbuffer::reset() {
  deleteCa();
  inx = outx = entries = 0;
}



inline bool StringRingbuffer::isEmpty() const {
  return entries == 0;
}



inline bool StringRingbuffer::isFull() const {
  return entries == len;
}



inline bool StringRingbuffer::in(const char *cp) {
  if (!isFull()) {
    char *ptr = new char[(strlen(cp) + 1) * sizeof(char)];
    strcpy(ptr, cp);
    ca[inx] = ptr;
    inx = (inx + 1) % len;
    entries++;
    return true;
  }
  return false;
}



inline bool StringRingbuffer::out(char *cp, int maxLength) {
  if (!isEmpty()) {
    int length = (strlen(ca[outx]) < maxLength ? strlen(ca[outx]) : maxLength);
    strncpy(cp, ca[outx], length);
    cp[length] = '\0';           // garantee terminator after strncpy
    delete[] ca[outx];
    ca[outx] = 0;
    outx = (outx + 1) % len;
    entries--;
    return true;
  }
  return false;
}



void StringRingbuffer::print() const {
  cout << "inx =     " << inx << endl;
  cout << "outx =    " << outx << endl;
  cout << "entries = " << entries << endl;

  for (int i = outx; i < outx + entries; ++i) {
    cout << i % len << ": " << ca[i % len] << endl;
  }
}





void inoutTest() {
  cout << "START" << endl;
  StringRingbuffer rb;
  rb.print();

  const char *ca[] = {
    "00000000000",
    "1111111111",
    "222222222",
    "33333333",
    "4444444",
    "555555",
    "66666",
    "7777",
    "888",
    "99",
  };


  for (int i = 0; i < 7; ++i) {
    cout << endl << "IN" << endl;
    rb.in(ca[i]);
    rb.print();
  }

  const int len = 80;
  char carr[len + 1];

  cout << endl << "OUT" << endl;
  rb.out(carr, len);
  cout << "   carr: " << carr << endl;
  rb.print();

  cout << endl<< "IN" << endl;
  rb.in(ca[9]);
  rb.print();

  cout << endl << "OUT" << endl;
  rb.out(carr, len);
  cout << "   carr: " << carr << endl;
  rb.print();

  cout << endl << "IN" << endl;
  rb.in(ca[8]);
  rb.print();


  cout << endl << "RESET" << endl;
  rb.reset();
  rb.print();
}



void assignTest() {
  StringRingbuffer rb2, rb3;

  const int len = 80;
  char carr[len + 1];

  {
    StringRingbuffer rb1;
    rb1.in("00000000");
    rb1.in("111111");
    rb1.in("222");

    cout << endl << "RB1" << endl;
    rb1.print();

    rb2 = rb1;
    rb3 = rb1;

    rb1.in("3");
    rb3 = rb1;
  }

  cout << endl << "COPY RB2" << endl;
  rb2.print();

  cout << endl << "COPY RB3" << endl;
  rb3.print();

  cout << endl << "OUT RB2" << endl;
  rb2.out(carr, len);
  rb2.print();

  cout << endl << "RB2" << endl;
  rb2.print();

  cout << endl << "RB3" << endl;
  rb3.print();
}



void copyTest() {
  StringRingbuffer rb1;

  const int len = 80;
  char carr[len + 1];

  rb1.in("00000");
  rb1.in("11111");
  rb1.in("22222");
  rb1.out(carr, len);
  rb1.out(carr, len);
  rb1.in("33333");
  rb1.in("44444");
  rb1.in("55555");

  cout << endl << "RB1" << endl;
  rb1.print();

  StringRingbuffer rb2(rb1);
  cout << endl << "RB2" << endl;
  rb2.print();
}



void heapTest() {
  StringRingbuffer *rbp1;
  rbp1 = new StringRingbuffer;

  const int len = 80;
  char carr[len + 1];

  rbp1->in("00000");
  rbp1->in("11111");
  rbp1->in("22222");
  rbp1->out(carr, len);
  rbp1->out(carr, len);
  rbp1->in("33333");
  rbp1->in("44444");
  rbp1->in("55555");

  cout << endl << "RB1" << endl;
  rbp1->print();


  StringRingbuffer *rbp2 = new StringRingbuffer(*rbp1);

  cout << endl << "RB2" << endl;
  rbp2->print();

  cout << endl << "OUT RB1" << endl;
  rbp1->out(carr, len);
  cout << "   carr: " << carr << endl;
  rbp1->print();

  cout << endl << "RB2" << endl;
  rbp2->print();
}





int main() {
  inoutTest();
  assignTest();
  copyTest();
  heapTest();

  return 0;
}
