#include <type_traits>
#include <iostream>
#include <iomanip>
#include <cstddef>
using namespace std;

template<typename T> struct Node {
    Node(const T& v) : value_(v), next_(0L) {}
    T value_;
    Node* next_;
};

//lista wykorzystuje specjalny obszar pamięci (BUFFER)
template<typename T> class List {
public:
    static const size_t BUFFER_SIZE = 80;
    List() : buffer_(new std::byte[BUFFER_SIZE]), current_(buffer_), head_(nullptr) {  }
    // ~List() {
    //     while( head_ ) {
    //         Node<T>* next = head_->next_;
    //         head_->value_.~T(); //wola destruktor bez zwalniania pamieci
    //         head_ = next;
    //     }
    //     delete [] buffer_;
    // }
    void add(const T& t) {
        //bada czy element sie zmiesci
        if ( sizeof(Node<T>) > BUFFER_SIZE - (current_ - buffer_) )
            throw std::bad_alloc();

        Node<T>* node = new(current_) Node<T>(t);
        node->next_ = head_;
        head_ = node;
        current_ += sizeof(Node<T>);
    }
    void printElements() const {
        Node<T>* node = head_;
        for(int n = 0; node != 0L; ++n) {
            cout << n << ':' << node->value_ << ',';
            node = node->next_;
        }
        cout << endl;
    }
    void printBuf() const {
        for(size_t i=0;i<BUFFER_SIZE;++i) {
            cout << hex << setfill('0') << setw(2) << static_cast<unsigned int>(buffer_[i]) << " ";
        }
        cout << dec << endl;
    }
    ~List() { //wykorzystuje trejty
        delete_impl(std::is_trivially_destructible<T>());
        delete [] buffer_;
    }
private:
    void delete_impl(std::true_type) {
        cout << "do nothing destructor" << endl;
    }
    void delete_impl(std::false_type) {
        cout << "loop delete destructor" << endl;
        while( head_ ) {
            Node<T>* next = head_->next_;
            head_->value_.~T(); //wola destruktor
            head_ = next;
        }
    }
private:
    std::byte* buffer_;
    std::byte* current_;
    Node<T>* head_;
};

class Foo {
public:
    Foo(int i) : i_(i) {}
    ~Foo() { cout << "d-tor Foo " << i_ << endl; }
    int i_;
};
std::ostream& operator<<(std::ostream& o, const Foo& f) {
    o << "Foo:" << f.i_ << ";";
    return o;
}

int main() {
    List<int> l;
    l.add(1);
    l.add(2);
    l.printElements();
    l.printBuf();

    List<Foo> l2;
    l2.add(Foo(1));
    l2.add(Foo(2));
    l2.printElements();
    l2.printBuf();

    return 0;
}
