#include "Stack.h"
#include "StackNode.h"
#include <cstddef>
#include "assert.h"

Stack::Stack(payload_free_fn pfree) : head_(NULL), pfree_(pfree) {}

Stack::~Stack() {
    void *payload = NULL;
    while (!isEmpty()) {
        assert(pop(&payload));
        pfree_(payload);
    }
}

void Stack::push(void * const elem) {
    if (isEmpty()) {
        head_ = new StackNode(elem);
    } else {
        head_ = new StackNode(elem, head_);
    }
}

bool Stack::pop(void ** const out) {
    if (isEmpty()) {
        return false;
    } else {
        *out = head_->getPayload();
        /* Because head_ is a const StackNode*, this temporary variable must also be a const
         * StackNode*
         */
        const StackNode *tmp = head_;
        head_ = tmp->getNext();
        delete tmp;
        return true;
    }
}

bool Stack::peek(void ** const out) const {
    /* Note that we could make this argument have type
     *     void * const * const out
     * (it is a valid type) but then the const annotation on the second * would prevent us from
     * actually writing to the address stored in 'out'.
     */
    if (isEmpty()) {
        return false;
    } else {
        *out = head_->getPayload();
        return true;
    }
}

bool Stack::isEmpty() const {
    return (head_ == NULL);
}