//-----------------------------------------------------------------------------
// Flags       : clang-format SMTGSequencer
// Project     : SDK Core
//
// Category    : SDK Core Interfaces
// Filename    : pluginterfaces/base/funknownimpl.h
// Created by  : Steinberg, 10/2021
// Description : Steinberg Module Architecture Interface Implementation Helper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

//------------------------------------------------------------------------
#include "pluginterfaces/base/fplatform.h"
#include "pluginterfaces/base/funknown.h"
#include <atomic>
#include <type_traits>

//------------------------------------------------------------------------
#if !(SMTG_CPP11)
#error "C++11 is required for this header"
#endif

// clang-format off
/**
This header provides classes for working with FUnknown.

An interface which shall support Steinbergs Module Architecture should inherit from U::Unknown and provide a typedef
@c IID of type U::UID.

On OS X you can generate an U::UID and copy it to the clipboard with the following shell command:
@code
uuidgen | { read id; echo -n "using IID = U::UID<0x${id:0:8}, 0x${id:9:4}${id:14:4}, 0x${id:19:4}${id:24:4}, 0x${id:28:8}>;" ; } | pbcopy
@endcode

Example:
@code{.cpp}
struct IFoo : public U::Unknown
{
    // Use a generated random uid here.
    using IID = U::UID<0x01234567, 0x89012345, 0x67890123, 0x45678901>;

    virtual void bar () = 0;
};
@endcode

A class implementing the interface @c IFoo uses the U::Implements template to specify the
interfaces it implements. All interfaces which the class should derive from need to be listed in the
U::Directly template.

Example:
@code{.cpp}
struct FooImpl : public U::Implements<U::Directly<IFoo>>
{
    void bar () override {}
};
@endcode

To check if a class can provide a specific interface use the U::cast function.

Example:
@code{.cpp}
void test (U::Unknown* obj)
{
    if (auto foo = U::cast<IFoo> (obj))
    {
        // obj provided IFoo
    }
}
@endcode

The U::Implements class supports a second template parameter U::Indirectly for specifying
a list of interfaces which should be available via @c queryInterface but not inherited from.
This is useful if an interface extends another interface.

Example:
@code{.cpp}
struct IBar : public IFoo
{
    using IID = U::UID<0x11223344, 0x55667788, 0x99001122, 0x33445566>;

    virtual void baz () = 0;
};

struct BarImpl : public U::Implements<U::Directly<IBar>, U::Indirectly<IFoo>>
{
    void bar () override {}
    void baz () override {}
};
@endcode

In some cases a class shall be extended and an additional interface implemented.
This is possible with the U::Extends template which is a generalization of the U::Implements
template and allows specifying a base class from which should be inherited.

Example:
@code{.cpp}
struct ITest : public U::Unknown
{
    using IID = U::UID<0x99887766, 0x55443322, 0x11009988, 0x77665544>;

    virtual bool equal (int a, int b) const = 0;
};

struct TestImpl : public U::Extends<FooImpl, U::Directly<ITest>>
{
    bool equal (int a, int b) const override { return a == b; }
};
@endcode

To pass arbitrary arguments to the specified base class one can use the inherited @c Base
typedef. All arguments passed to @c Base are automatically perfectly forwarded to the base class.

In the following example the value 42 is passed to the @c AlternativeFooImpl base class:
@code{.cpp}
struct AlternativeFooImpl : public U::Implements<U::Directly<IFoo>>
{
    AlternativeFooImpl (int dummy = 0) : dummy {dummy} {}
    void bar () override {}

    int dummy;
};

struct AlternativeTestImpl : public U::Extends<AlternativeFooImpl, U::Directly<ITest>>
{
    AlternativeTestImpl () : Base {42} {}

    bool equal (int a, int b) const override { return a == b; }
};
@endcode
*/
// clang-format on

//------------------------------------------------------------------------
namespace Steinberg {
namespace FUnknownImpl {

/** Typedef to keep everything in this namespace. */
using Unknown = FUnknown;

/** A base class which hides the FUnknown::iid static var */
struct HideIIDBase : FUnknown
{
	using iid = void;
};

/** Common destroyer policy for ski object instances.*/
struct Destroyer
{
	template <typename UnknownT>
	static void destroy (UnknownT* ptr)
	{
		if (!!ptr)
			ptr->release ();
	}
};

template <typename Base, typename D, typename I>
class ImplementsImpl;

/**
 *  This class provides a compile-time uid and enables  interfaces to specify a UID as a simple
 *  typedef. This way the FUID, DECLARE_CLASS_IID and DEF_CLASS_IID code can be omitted.
 */
template <uint32 t1, uint32 t2, uint32 t3, uint32 t4>
struct UID
{
	enum : int8
	{
		l1_1 = static_cast<int8> ((t1 & 0xFF000000) >> 24),
		l1_2 = static_cast<int8> ((t1 & 0x00FF0000) >> 16),
		l1_3 = static_cast<int8> ((t1 & 0x0000FF00) >> 8),
		l1_4 = static_cast<int8> ((t1 & 0x000000FF)),
		l2_1 = static_cast<int8> ((t2 & 0xFF000000) >> 24),
		l2_2 = static_cast<int8> ((t2 & 0x00FF0000) >> 16),
		l2_3 = static_cast<int8> ((t2 & 0x0000FF00) >> 8),
		l2_4 = static_cast<int8> ((t2 & 0x000000FF)),
		l3_1 = static_cast<int8> ((t3 & 0xFF000000) >> 24),
		l3_2 = static_cast<int8> ((t3 & 0x00FF0000) >> 16),
		l3_3 = static_cast<int8> ((t3 & 0x0000FF00) >> 8),
		l3_4 = static_cast<int8> ((t3 & 0x000000FF)),
		l4_1 = static_cast<int8> ((t4 & 0xFF000000) >> 24),
		l4_2 = static_cast<int8> ((t4 & 0x00FF0000) >> 16),
		l4_3 = static_cast<int8> ((t4 & 0x0000FF00) >> 8),
		l4_4 = static_cast<int8> ((t4 & 0x000000FF))
	};

	UID () = delete;

	static constexpr TUID data = {
#if COM_COMPATIBLE
	    l1_4, l1_3, l1_2, l1_1, l2_2, l2_1, l2_4, l2_3,
#else
	    l1_1, l1_2, l1_3, l1_4, l2_1, l2_2, l2_3, l2_4,
#endif
	    l3_1, l3_2, l3_3, l3_4, l4_1, l4_2, l4_3, l4_4,
	};

	static const TUID& toTUID () { return data; }
};

/** @return the TUID for an interface. */
template <typename T>
const TUID& getTUID ()
{
	return ::Steinberg::getTUID<T> ();
}

/**
 *  Checks if the given Unknown can provide the specified interface and returns it in an IPtr.
 *
 *  @return an IPtr pointing to an instance of the requested interface or nullptr in case the
 *          object does not provide the interface.
 */
template <typename I>
IPtr<I> cast (Unknown* u)
{
	I* out = nullptr;
	return u && u->queryInterface (getTUID<I> (), reinterpret_cast<void**> (&out)) == kResultOk ?
	           owned (out) :
	           nullptr;
}

/** Casts to Unknown* and then to the specified interface. */
template <typename I, typename S, typename T, typename U>
IPtr<I> cast (ImplementsImpl<S, T, U>* u)
{
	return cast<I> (u->unknownCast ());
}

/** Casts to Unknown* and then to the specified interface. */
template <typename I, typename T>
IPtr<I> cast (const IPtr<T>& u)
{
	return cast<I> (u.get ());
}

//------------------------------------------------------------------------
namespace Detail {

/**
 *  This struct implements reference counting for the @c U::Implements template.
 *  It also provides a @c queryInterface method stub to support @c queryInterface
 *  call made in the @c U::Implements template.
 */
struct RefCounted
{
//------------------------------------------------------------------------
	RefCounted () = default;
	RefCounted (const RefCounted&) {}
	RefCounted (RefCounted&& other) SMTG_NOEXCEPT : refCount {other.refCount.load ()} {}
	virtual ~RefCounted () = default;

	RefCounted& operator= (const RefCounted&) { return *this; }
	RefCounted& operator= (RefCounted&& other) SMTG_NOEXCEPT
	{
		refCount = other.refCount.load ();
		return *this;
	}

	uint32 PLUGIN_API addRef () { return ++refCount; }

	uint32 PLUGIN_API release ()
	{
		auto rc = --refCount;
		if (rc == 0)
		{
			destroyInstance ();
			refCount = -1000;
			delete this;
			return uint32 ();
		}
		return rc;
	}

//------------------------------------------------------------------------
private:
	virtual void destroyInstance () {}

	std::atomic<int32> refCount {1};
};

//------------------------------------------------------------------------
struct NonDestroyable
{
//------------------------------------------------------------------------
	NonDestroyable () = default;
	virtual ~NonDestroyable () = default;
	uint32 PLUGIN_API addRef () { return 1000; }
	uint32 PLUGIN_API release () { return 1000; }

private:
	virtual void destroyInstance () {}
};

//------------------------------------------------------------------------
template <typename T>
struct QueryInterfaceEnd : T
{
//------------------------------------------------------------------------
	tresult PLUGIN_API queryInterface (const TUID /*iid*/, void** obj)
	{
		*obj = nullptr;
		return kNoInterface;
	}
//------------------------------------------------------------------------
};

//------------------------------------------------------------------------
} // Detail

/**
 *  This struct is used to group a list of interfaces from which should be inherited and which
 *  should be available via the @c queryInterface method.
 */
template <typename... T>
struct Directly
{
};

/**
 *  This struct is used to group a list of interfaces from which should not be inherited but which
 *  should be available via the @c queryInterface method.
 */
template <typename... T>
struct Indirectly
{
};

template <typename Base, typename D, typename I>
class ImplementsImpl
{
	static_assert (sizeof (Base) == -1, "use U::Directly and U::Indirectly to specify interfaces");
};

template <typename Base, typename... DirectInterfaces, typename... IndirectInterfaces>
class ImplementsImpl<Base, Indirectly<IndirectInterfaces...>, Directly<DirectInterfaces...>>
{
	static_assert (sizeof (Base) == -1, "U::Indirectly only allowed after U::Directly");
};

/** This class implements the required virtual methods for the U::Unknown class. */
template <typename BaseClass, typename I, typename... DirectIFs, typename... IndirectIFs>
class ImplementsImpl<BaseClass, Directly<I, DirectIFs...>, Indirectly<IndirectIFs...>>
: public BaseClass, public I, public DirectIFs...
{
public:
//------------------------------------------------------------------------
	/**
	 *  This is a convenience typedef for the deriving class to pass arguments to the
	 *  constructor, which are in turn passed to the base class of this class.
	 */
	using Base = ImplementsImpl<BaseClass, Directly<I, DirectIFs...>, Indirectly<IndirectIFs...>>;

	template <typename... Args>
	ImplementsImpl (Args&&... args) : BaseClass {std::forward<Args> (args)...}
	{
	}

	tresult PLUGIN_API queryInterface (const TUID tuid, void** obj) override
	{
		if (!obj)
			return kInvalidArgument;

		if (queryInterfaceImpl<I, DirectIFs...> (tuid, *obj) ||
		    queryInterfaceImpl<IndirectIFs...> (tuid, *obj))
		{
			static_cast<Unknown*> (*obj)->addRef ();
			return kResultOk;
		}

		return BaseClass::queryInterface (tuid, obj);
	}

	uint32 PLUGIN_API addRef () override { return BaseClass::addRef (); }
	uint32 PLUGIN_API release () override { return BaseClass::release (); }

	Unknown* unknownCast () { return static_cast<Unknown*> (static_cast<I*> (this)); }

//------------------------------------------------------------------------
private:
	template <typename Interface>
	inline constexpr bool match (const TUID tuid) const noexcept
	{
		return reinterpret_cast<const uint64*> (tuid)[0] ==
		           reinterpret_cast<const uint64*> (getTUID<Interface> ())[0] &&
		       reinterpret_cast<const uint64*> (tuid)[1] ==
		           reinterpret_cast<const uint64*> (getTUID<Interface> ())[1];
	}

	template <int = 0>
	inline constexpr bool queryInterfaceImpl (const TUID, void*&) const noexcept
	{
		return false;
	}

	template <typename Interface, typename... RemainingInterfaces>
	inline bool queryInterfaceImpl (const TUID tuid, void*& obj) noexcept
	{
		if (match<Interface> (tuid) || match<Unknown> (tuid))
		{
			obj = static_cast<Interface*> (this);
			return true;
		}

		obj = getInterface<RemainingInterfaces...> (tuid);
		return obj != nullptr;
	}

	template <int = 0>
	inline constexpr void* getInterface (const TUID) const noexcept
	{
		return nullptr;
	}

	template <typename Interface, typename... RemainingInterfaces>
	inline void* getInterface (const TUID tuid) noexcept
	{
		return match<Interface> (tuid) ? static_cast<Interface*> (this) :
		                                 getInterface<RemainingInterfaces...> (tuid);
	}
};

/** This typedef enables using a custom base class with the interface implementation. */
template <typename BaseClass, typename D, typename I = Indirectly<>>
using Extends = ImplementsImpl<BaseClass, D, I>;

/** This typedef provides the interface implementation. */
template <typename D, typename I = Indirectly<>>
using Implements = ImplementsImpl<Detail::QueryInterfaceEnd<Detail::RefCounted>, D, I>;

/** This typedef provides the interface implementation for objects which should not be destroyed via
 *	FUnknown::release (like singletons). */
template <typename D, typename I = Indirectly<>>
using ImplementsNonDestroyable =
    ImplementsImpl<Detail::QueryInterfaceEnd<Detail::NonDestroyable>, D, I>;

//------------------------------------------------------------------------
} // FUnknownImpl

//------------------------------------------------------------------------
/** Shortcut namespace for implementing FUnknown based objects. */
namespace U {

using Unknown = FUnknownImpl::HideIIDBase;
using FUnknownImpl::UID;
using FUnknownImpl::Extends;
using FUnknownImpl::Implements;
using FUnknownImpl::ImplementsNonDestroyable;
using FUnknownImpl::Directly;
using FUnknownImpl::Indirectly;
using FUnknownImpl::cast;
using FUnknownImpl::getTUID;

//------------------------------------------------------------------------
} // namespace U
} // namespace Steinberg
