/*
 * Container.h    Step 7, Storage management redesigned.
 */
#ifndef CONTAINER_H
#define CONTAINER_H
#include <cstdlib>

template <typename T>
class Container
{
public:
   using value_type             = T;
   using pointer                = value_type*;
   using const_pointer          = const value_type*;
   using reference              = value_type&;
   using const_reference        = const value_type&;
   using size_type              = std::size_t;
   using iterator               = T*;
   using const_iterator         = const T*;
   using reverse_iterator       = std::reverse_iterator<iterator>;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
   using difference_type        = std::ptrdiff_t;

   Container() noexcept = default;
   explicit Container(const size_type n);
   Container(const Container&);
   Container(Container&&) noexcept;

   ~Container();

   Container& operator=(const Container&) &;
   Container& operator=(Container&&) & noexcept;

   size_type size() const noexcept;
   size_type max_size() const noexcept;
   size_type capacity() const noexcept;
   bool      empty() const noexcept;
   void      clear();

   void      push_back(const T&);
   T&        back();
   const T&  back() const;
   void      pop_back();

   void      swap(Container&) noexcept;

   iterator               begin() noexcept;
   const_iterator         begin() const noexcept;
   iterator               end() noexcept;
   const_iterator         end() const noexcept;

   reverse_iterator       rbegin() noexcept;
   const_reverse_iterator rbegin() const noexcept;
   reverse_iterator       rend() noexcept;
   const_reverse_iterator rend() const noexcept;

   const_iterator         cbegin() const noexcept;
   const_iterator         cend() const noexcept;
   const_reverse_iterator crbegin() const noexcept;
   const_reverse_iterator crend() const noexcept;

private:
   T* start_ = nullptr;
   T* finish_ = nullptr;
   T* end_of_storage_ = nullptr;

   size_type compute_capacity_() const;

   T*   allocate_(size_type);
   void deallocate_(T*);

   void construct_(T*, const T&);  // added
   void destroy_(T*);              // added
   void destroy_(T*, T*);          // added

   T*   allocate_and_copy_(const Container&);
   void resize_(const T&);
};

template <typename T>
void swap(Container<T>&, Container<T>&) noexcept;

#include "Container.tcc"

#endif