#pragma once

#include "ReferenceCounter.h"

namespace Framework
{
    template<class T> class Stack : public ReferenceCounter
    {
    private:
        std::function<T()> nullSupplier;
        T* data;
        int size;
        int capacity;

    public:
        Stack(std::function<T()> nullSupplier)
            : ReferenceCounter(),
              nullSupplier(nullSupplier),
              data(0),
              size(0),
              capacity(0)
        {}

        Stack(T null)
            : Stack([null]() { return null; })
        {}

        Stack()
            : Stack([]() { return (T)0; })
        {}

        ~Stack()
        {
            if (data)
            {
                delete[] data;
            }
        }

        void push(T element)
        {
            if (size == capacity)
            {
                int newCapacity
                    = capacity == 0
                        ? 1
                        : capacity * 2; // new variable is needed to avoid false
                                        // positive compiler warnings
                T* newData = new T[newCapacity];
                for (int i = 0; i < size; i++)
                {
                    newData[i] = data[i];
                }
                capacity = newCapacity;
                if (data)
                {
                    delete[] data;
                }
                data = newData;
            }
            data[size++] = element;
        }

        T pop()
        {
            if (size == 0)
            {
                return nullSupplier();
            }
            return data[--size];
        }

        T peek()
        {
            if (size == 0)
            {
                return nullSupplier();
            }
            return data[size - 1];
        }

        void reset()
        {
            size = 0;
        }

        int getSize()
        {
            return size;
        }

        int getCapacity()
        {
            return capacity;
        }

        T* getData()
        {
            return data;
        }
    };
} // namespace Framework