/*
 * Another variation on the linked stack, this time using the shared_ptr
 * library class to manage memory allocation.  We no longer need a 
 * destructor.
 */
#ifndef _lnkstack3_h_
#define _lnkstack3_h_
#include <utility>
#include <memory>
#include <iostream>
using std::shared_ptr;
template <typename T>
class Stack {
public:
        Stack() {
                m_size = 0;
                std::cout << "== Creating Stack ==" << std::endl;
        }
        
        // Clean up any remaining nodes.
        ~Stack() {
                // This destructor exists only to print.
                std::cout << "== Destroying Stack (" << m_size << " nodes) =="
                          << std::endl;
        }
        
        // Push the argument item.
        void push(const T &itm) {
                // Create a new node, holding it in a unique ptr.  (The
                // make_unique method is from the 2014 standandard.)
                m_head = std::make_shared<node>(itm, m_head);
                ++m_size;
        }
        // Pop the argument item, and return true, or return false if the
        // the stack is empty.
        bool pop(T &itm) {
                if(!m_head) return false;
                itm = m_head->data();
                m_head = m_head->next();
                --m_size;
                return true;
        }
        // Convenience to pop and return the top object.  Only works
        // for types where T() is a valid expression.
        T pop() {
                T ret;
                if(pop(ret)) return ret;
                return T();
        }
        // Tell if empty.
        bool empty() {
                return m_head == nullptr;
        }
        // Report the size
        int size() {
                return m_size;
        }
        
private:
        // Nodes for the linked list.
        class node {
        public:
                // Constructor for a node.  Saves the arguments.
                node(const T &d, shared_ptr<node> n = nullptr) {
                        m_data = d;
                        m_next = n;
                        std::cout << "== Creating node [" << d << "] =="
                                  << std::endl;
                }
                ~node() {
                        // This destructor exists only to print.
                        std::cout << "== Destroying node [" << m_data << "] =="
                                  << std::endl;
                }
                const T& data() { return m_data; }
                shared_ptr<node> next() { return m_next; }
        private:
                T m_data;
                shared_ptr<node> m_next;
        };
        shared_ptr<node> m_head;
        int m_size;
};
#endif