#include <iostream>
#include <cmath>

using namespace std;

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

template <typename T> class TArray
{
  public:
    TArray(int arrSize = 10);
    TArray(const TArray &z);
    ~TArray();

    int getLength() const;
    int getMaxLength() const;

    TArray & operator =(const TArray &z);
    T & operator [](int k);

    void addItem(T item);
    void deleteItem();
    void insertItem(int k, T item);
    void removeItem(int k);

    void doForEach(void (* f)(T &z));

  private:
    T *arr;
    int length;
    int maxLength;

    void arrInit(int arrSize);
    void arrDelete();
    void arrCopy(const TArray &z);
};

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

template <typename T> TArray<T>::TArray(int arrSize)
{
  arrInit(arrSize);
}

template <typename T> TArray<T>::TArray(const TArray &z)
{
  arrCopy(z);
}

template <typename T> TArray<T>::~TArray()
{
  arrDelete();
}

template <typename T> TArray<T> & TArray<T>::operator =(const TArray &z)
{
  if(this != &z)
  {
    arrDelete();
    arrCopy(z);
  }

  return *this;
}

template <typename T> T & TArray<T>::operator [](int k)
{
  return arr[k];
}

template <typename T> int TArray<T>::getLength() const
{
  return length;
}

template <typename T> int TArray<T>::getMaxLength() const
{
  return maxLength;
}

template <typename T> void TArray<T>::addItem(T item)
{
  if(length < maxLength)
  {
    arr[length] = item;
    length++;
  }
}

template <typename T> void TArray<T>::deleteItem()
{
  if(length > 0)
    length--;
}

template <typename T> void TArray<T>::insertItem(int k, T item)
{
  if(k >= 0 && k < length)
  {
    for(int i = length; i >= k; i--)
      arr[i + 1] = arr[i];

    arr[k] = item;
    length++;
  }
}

template <typename T> void TArray<T>::removeItem(int k)
{
  if(k >= 0 && k < length)
  {
    for(int i = k; i < length - 1; i++)
      arr[i] = arr[i + 1];
 
   length--;
  }
}

template <typename T> void TArray<T>::doForEach(void (* f)(T &z))
{
  for(int i = 0; i < length; i++)
    f(arr[i]);
}

template <typename T> void TArray<T>::arrInit(int arrSize)
{
  arr = nullptr;
  length = 0;
  maxLength = 0;

  if(arrSize > 0)
  {
    arr = new T[arrSize];
    maxLength = arrSize;
  }
}

template <typename T> void TArray<T>::arrDelete()
{
  if(arr)
  {
    delete [] arr;
  }

  arr = nullptr;
  length = 0;
  maxLength = 0;
}

template <typename T> void TArray<T>::arrCopy(const TArray &z)
{
  if(z.maxLength > 0)
  {
    arrInit(z.maxLength);

    for(int i = 0; i < z.maxLength; i++)
      arr[i] = z.arr[i];

    length = z.length;
  }
  else
    arrDelete();
}

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

template <typename T> void printItem(T &item)
{
  cout << "Item = " << item << endl;
}

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

int main()
{
  TArray<int> seq(10);

  cout << "Po deklaracji seq(10):" << endl;
  cout << "seq.getLength=" << seq.getLength() << endl;
  cout << "seq.getMaxLength=" << seq.getMaxLength() << endl << endl;

  seq.addItem(1);

  cout << "Po dodaniu 1 elementu seq.addItem(1):" << endl;
  cout << "seq.getLength=" << seq.getLength() << endl;
  cout << "seq.getMaxLength=" << seq.getMaxLength() << endl << endl;

  seq.addItem(2);
  seq.addItem(3);
  seq.addItem(4);
  seq.addItem(5);

  cout << "Po dodaniu 4 elementow seq.addItem(2) ... seq.addItem(5):" << endl;
  cout << "seq.getLength=" << seq.getLength() << endl;
  cout << "seq.getMaxLength=" << seq.getMaxLength() << endl << endl;

  cout << "Sprawdzenie operatora indeksowania, cout << seq[2]:" << endl;

  cout << seq[2] << endl << endl;

  cout << "Sprawdzenie operatora indeksowania, zmiana wartosci seq[2] = 7 i cout << seq[2]:" << endl;

  seq[2] = 7;

  cout << seq[2] << endl << endl;

  TArray<int> vec(seq);

  cout << "Nowa talica, konstruktor kopiujacy, TArray<int> vec(seq):" << endl;
  cout << "vec.getLength=" << vec.getLength() << endl;
  cout << "vec.getMaxLength=" << vec.getMaxLength() << endl << endl;

  seq.deleteItem();

  cout << "Po skasowaniu ostatniego elementu seq.deleteItem():" << endl;
  cout << "seq.getLength=" << seq.getLength() << endl;
  cout << "seq.getMaxLength=" << seq.getMaxLength() << endl << endl;

  seq.insertItem(3, 777);

  cout << "Po dolozeniu elementu seq.insertItem(3, 777) i << seq[3]:" << endl;
  cout << "seq[3]=" << seq[3] << endl;
  cout << "seq.getLength=" << seq.getLength() << endl;
  cout << "seq.getMaxLength=" << seq.getMaxLength() << endl << endl;

  seq.removeItem(3);

  cout << "Po usunieciu elementu 3, seq.removeItem(3) i << seq[3]:" << endl;
  cout << "seq[3]=" << seq[3] << endl;
  cout << "seq.getLength=" << seq.getLength() << endl;
  cout << "seq.getMaxLength=" << seq.getMaxLength() << endl << endl;

  cout << "Sprawdzenie funkcji, printItem<int>(seq[2]):" << endl;

  printItem<int>(seq[2]);

  cout << endl;

  cout << "Sprawdzenie funkcji, seq.doForEach(printItem<int>):" << endl;

  seq.doForEach(printItem<int>);

  cout << endl;

  cout << endl << endl;

  return 0;
}
