#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <utility>
#include <memory>
#include "sharedptr.h"
using namespace std;

auto create_vec1() {

    // SharedPtr<T> replaces T* for heap-allocated object.
    // That is, instead of doing
    //
    //     string* p { new string{"hello"} };
    //
    // We do the following:

    SharedPtr<string> p1 { new string{"hello"} };
    SharedPtr<string> p2 { new string{"c"} };
    SharedPtr<string> p3 { new string{"students"} };

    vector v { p1, p2, p3 };
    return v;
}

template <typename T, typename... Args>
SharedPtr<T> MakeSharedPtr(Args&&... args) {
    return SharedPtr<T>{new T{forward<Args>(args)...}};
}

auto create_vec2() {
    auto p1 { MakeSharedPtr<string>("hello") };
    auto p2 { MakeSharedPtr<string>("c") };
    auto p3 { MakeSharedPtr<string>("students") };

    vector v { p1, p2, p3 };
    return v;
}

auto create_vec3() {
    auto p1 { make_shared<string>("hello") };
    auto p2 { make_shared<string>("c") };
    auto p3 { make_shared<string>("students") };
    static_assert(is_same_v<decltype(p3), shared_ptr<string>>);

    vector v { p1, p2, p3 };
    return v;
}

int main() {
    vector v1 = create_vec1();
    // vector v1 = create_vec2();
    // vector v1 = create_vec3();

    for (auto p : v1) { cout << *p + " "; } cout << '\n';
    // for (auto& p : v1) { cout << *p + " "; } cout << '\n';
    // for (const auto& p : v1) { cout << *p + " "; } cout << '\n';
    // for (auto&& p : v1) { cout << *p + " "; } cout << '\n';

    vector v2 = v1;
    *v2[1] = "c2cpp";
    *v2[2] = "hackers"; 

    // Print v1 again
    for (auto&& p : v1) { cout << *p + " "; } cout << '\n';

    cout << "\nReference count demo:\n";
    {
        SharedPtr<string> p1 { new string{"hello"} };
        cout << "before foo(), reference count: " << p1.get_count() << '\n';

        auto foo = [](SharedPtr<string> p) {
            cout << "inside foo(), reference count: " << p.get_count() << '\n';
        };
        foo(p1);

        cout << "after foo(), reference count: " << p1.get_count() << '\n';
    }
}
