// Pruebas públicas de IntStack. Cada una se corre aparte: test_int_stack nombre.
#include <cassert>
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>

#include "alloc_count.h"
#include "int_stack.h"

namespace {

void test_basic() {
    IntStack s(3);
    assert(s.empty() && !s.full());
    assert(s.size() == 0 && s.capacity() == 3);
    s.push(1);
    s.push(2);
    s.push(3);
    assert(s.full() && !s.empty() && s.size() == 3);
    assert(s.top() == 3);
    s.pop();
    assert(s.top() == 2 && !s.full());
    s.pop();
    s.pop();
    assert(s.empty());
    s.push(9);
    assert(s.top() == 9);
}

void test_reserves_once() {
    std::size_t before = array_news();
    {
        IntStack s(100);
        for (int i = 0; i < 100; i++) {
            s.push(i);
        }
        assert(array_news() == before + 1);
    }
    assert(array_news() - before == 1);
}

void test_zero_capacity() {
    IntStack s(0);
    assert(s.empty() && s.full() && s.capacity() == 0);
}

void test_copy_constructor() {
    IntStack a(4);
    a.push(1);
    a.push(2);
    IntStack b = a;
    assert(b.size() == 2 && b.capacity() == 4 && b.top() == 2);
    b.pop();
    b.push(50);
    assert(a.top() == 2 && b.top() == 50);
    a.pop();
    assert(a.top() == 1 && b.size() == 2);
}

void test_copy_assignment() {
    IntStack a(4);
    a.push(1);
    a.push(2);
    IntStack b(1);
    b.push(7);
    b = a;
    assert(b.size() == 2 && b.capacity() == 4 && b.top() == 2);
    b.pop();
    b.push(50);
    assert(a.top() == 2 && b.top() == 50);
}

void test_self_assignment() {
    IntStack a(4);
    a.push(1);
    a.push(2);
    IntStack& same = a;
    a = same;
    assert(a.size() == 2 && a.capacity() == 4 && a.top() == 2);
    a.pop();
    assert(a.top() == 1);
}

void test_move_constructor() {
    IntStack a(4);
    a.push(1);
    a.push(2);
    std::size_t before = array_news();
    IntStack b = std::move(a);
    assert(array_news() == before);
    assert(b.size() == 2 && b.capacity() == 4 && b.top() == 2);
    assert(a.empty() && a.capacity() == 0);
}

void test_move_assignment() {
    IntStack a(4);
    a.push(1);
    a.push(2);
    IntStack b(9);
    b.push(7);
    std::size_t before = array_news();
    b = std::move(a);
    assert(array_news() == before);
    assert(b.size() == 2 && b.capacity() == 4 && b.top() == 2);
    assert(a.empty() && a.capacity() == 0);
}

void test_self_move() {
    IntStack a(4);
    a.push(1);
    IntStack& same = a;
    a = std::move(same);
    assert(a.size() == 1 && a.capacity() == 4 && a.top() == 1);
}

void test_moved_from_is_reusable() {
    IntStack a(4);
    a.push(1);
    IntStack b = std::move(a);
    assert(a.full());
    a = b;
    assert(a.size() == 1 && a.top() == 1);
    a.push(2);
    assert(a.top() == 2 && b.size() == 1);
}

void test_noexcept() {
    IntStack a(1);
    IntStack b = std::move(a);
    assert(a.capacity() == 0 && b.capacity() == 1);
    assert(std::is_nothrow_move_constructible_v<IntStack>);
    assert(std::is_nothrow_move_assignable_v<IntStack>);
}

}  // namespace

int main(int argc, char* argv[]) {
    std::string name = argc > 1 ? argv[1] : "";
    if (name == "basic") test_basic();
    else if (name == "reserves_once") test_reserves_once();
    else if (name == "zero_capacity") test_zero_capacity();
    else if (name == "copy_constructor") test_copy_constructor();
    else if (name == "copy_assignment") test_copy_assignment();
    else if (name == "self_assignment") test_self_assignment();
    else if (name == "move_constructor") test_move_constructor();
    else if (name == "move_assignment") test_move_assignment();
    else if (name == "self_move") test_self_move();
    else if (name == "moved_from_is_reusable") test_moved_from_is_reusable();
    else if (name == "noexcept") test_noexcept();
    else {
        std::cerr << "prueba desconocida: " << name << "\n";
        return 2;
    }
    return 0;
}
