#pragma once

#include "catch.hpp"

// Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that
// the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that
// require CopyConstructible (e.g. for wistd::function)
struct fail_on_copy
{
    fail_on_copy() = default;

    fail_on_copy(const fail_on_copy&)
    {
        FAIL("Copy constructor invoked for fail_on_copy type");
    }

    fail_on_copy(fail_on_copy&&) = default;

    fail_on_copy& operator=(const fail_on_copy&)
    {
        FAIL("Copy assignment operator invoked for fail_on_copy type");
        return *this;
    }

    fail_on_copy& operator=(fail_on_copy&&) = default;
};

// Useful for validating that objects get copied e.g. as opposed to capturing a reference
struct value_holder
{
    int value = 0xbadf00d;

    ~value_holder()
    {
        value = 0xbadf00d;
    }
};

// Useful for validating that functions, etc. are callable with move-only types
// Example real type that is move only is Microsoft::WRL::Wrappers::HString
struct cannot_copy
{
    cannot_copy() = default;
    cannot_copy(const cannot_copy&) = delete;
    cannot_copy& operator=(const cannot_copy&) = delete;

    cannot_copy(cannot_copy&&) = default;
    cannot_copy& operator=(cannot_copy&&) = default;
};

// State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in
// contexts that require a default constructible type, but has the nice property that it allows for tests to run
// concurrently
struct object_counter_state
{
    volatile LONG constructed_count = 0;
    volatile LONG destructed_count = 0;
    volatile LONG copy_count = 0;
    volatile LONG move_count = 0;

    LONG instance_count()
    {
        return constructed_count - destructed_count;
    }
};

struct object_counter
{
    object_counter_state* state;

    object_counter(object_counter_state& s) :
        state(&s)
    {
        ::InterlockedIncrement(&state->constructed_count);
    }

    object_counter(const object_counter& other) :
        state(other.state)
    {
        ::InterlockedIncrement(&state->constructed_count);
        ::InterlockedIncrement(&state->copy_count);
    }

    object_counter(object_counter&& other) WI_NOEXCEPT :
        state(other.state)
    {
        ::InterlockedIncrement(&state->constructed_count);
        ::InterlockedIncrement(&state->move_count);
    }

    ~object_counter()
    {
        ::InterlockedIncrement(&state->destructed_count);
        state = nullptr;
    }

    object_counter& operator=(const object_counter&)
    {
        ::InterlockedIncrement(&state->copy_count);
        return *this;
    }

    object_counter& operator=(object_counter&&) WI_NOEXCEPT
    {
        ::InterlockedIncrement(&state->move_count);
        return *this;
    }
};