// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE

#pragma once

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <utility>

//-----------------------------------------------------------------------------
// VSTGUI Version
//-----------------------------------------------------------------------------
#define VSTGUI_VERSION_MAJOR  4
#define VSTGUI_VERSION_MINOR  15
#define VSTGUI_VERSION_PATCHLEVEL  0

//-----------------------------------------------------------------------------
// Platform definitions
//-----------------------------------------------------------------------------
#if __APPLE_CC__
	#ifndef __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES
		#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
	#endif
	#include <stdint.h>
	#include <AvailabilityMacros.h>
	#include <TargetConditionals.h>
	#if TARGET_OS_IPHONE
		#ifndef MAC
			#define MAC 1
		#endif
		#ifndef MAC_COCOA
			#define MAC_COCOA 1
		#endif
		#define VSTGUI_OPENGL_SUPPORT 0	// there's an implementation, but not yet tested, so this is zero
		#define VSTGUI_TOUCH_EVENT_HANDLING 1
	#else
		#ifndef MAC_OS_X_VERSION_10_9
			#error you need at least OSX SDK 10.9 to build vstgui
		#endif
		#ifndef MAC_COCOA
			#define MAC_COCOA 1
		#endif
		#ifndef MAC
			#define MAC 1
		#endif
	#endif

	#ifndef __has_feature
		#error compiler not supported
	#endif
	#if __has_feature (cxx_rvalue_references) == 0
		#error need cxx_rvalue_references support from compiler
	#endif
	#if __has_feature (cxx_range_for) == 0
		#error need cxx_range_for support from compiler
	#endif
	#include <type_traits>

	#if defined (__clang__) && __clang_major__ > 4
		#if defined (VSTGUI_WARN_EVERYTHING) && VSTGUI_WARN_EVERYTHING == 1
			#pragma clang diagnostic warning "-Weverything"
			#pragma clang diagnostic warning "-Wconversion"
			#pragma clang diagnostic ignored "-Wreorder"
		#else
			#pragma clang diagnostic warning "-Wunreachable-code"
			#if __clang_major__ > 7
				#pragma clang diagnostic warning "-Wshadow"
			#endif
		#endif
	#endif

#elif WIN32 || WINDOWS || defined(_WIN32)
	#ifndef NOMINMAX
		#define NOMINMAX
	#endif
	#ifndef WIN32_LEAN_AND_MEAN
		#define WIN32_LEAN_AND_MEAN
	#endif

	#include <sdkddkver.h>
	#if _WIN32_WINNT < 0x601
		#error unsupported Platform SDK you need at least the Windows 7 Platform SDK to compile VSTGUI
	#endif
	#ifdef __GNUC__
		#if __cplusplus < 201103L
			#error compiler not supported
		#endif
	#elif _MSC_VER <= 1800
		#error Visual Studio 2015 or newer needed
	#endif
	#include <type_traits>
	#include <cstdint>
	#ifndef WINDOWS
		#define WINDOWS 1
	#endif
	#if (defined(_M_ARM64) || defined(_M_ARM))
		#if defined(VSTGUI_OPENGL_SUPPORT)
			#undef VSTGUI_OPENGL_SUPPORT
		#endif
		#define VSTGUI_OPENGL_SUPPORT 0
	#endif
	#ifdef _MSC_VER
		#pragma warning(3 : 4189) // local variable is initialized but not referenced
		#pragma warning(3 : 4702) // unreachable code
		#pragma warning(3 : 4995) // deprecated
		#pragma warning(3 : 4431) // missing type specifier - int assumed. Note: C no longer supports default-int
		#pragma warning(3 : 4254) // conversion from 'type1' to 'type2', possible loss of data
		#pragma warning(3 : 4388) // signed/unsigned mismatch
		#pragma warning(disable : 4250) // class1' : inherits 'class2::member' via dominance
	#endif

	#if defined (__clang__) && __clang__
		#if defined (VSTGUI_WARN_EVERYTHING) && VSTGUI_WARN_EVERYTHING == 1
			#pragma clang diagnostic warning "-Wconversion"
			#pragma clang diagnostic ignored "-Wreorder"
		#else
			#pragma clang diagnostic warning "-Wunreachable-code"
		#endif
	#endif

	#include <algorithm>
	using std::min;
	using std::max;

#elif defined(__linux__)
    #include <cstdint>
    #include <type_traits>
    #include <algorithm>
    #include <climits>
    using std::min;
    using std::max;
	#ifndef LINUX
		#define LINUX 1
	#endif

#else
#error unsupported system/compiler
#endif

#include <atomic>
#include <utility>

#ifdef UNICODE
	#undef UNICODE
#endif
#define UNICODE 1

//----------------------------------------------------
// Deprecation setting
//----------------------------------------------------
#ifndef VSTGUI_ENABLE_DEPRECATED_METHODS
	#define VSTGUI_ENABLE_DEPRECATED_METHODS 1
#endif

#if VSTGUI_ENABLE_DEPRECATED_METHODS
	#define VSTGUI_DEPRECATED(x)			[[deprecated]]	x
	#define VSTGUI_DEPRECATED_MSG(x, msg)	[[deprecated(msg)]]	x
#else
	#define VSTGUI_DEPRECATED(x)
	#define VSTGUI_DEPRECATED_MSG(x, msg)
#endif

//----------------------------------------------------
// Feature setting
//----------------------------------------------------
#ifndef VSTGUI_OPENGL_SUPPORT
	#define VSTGUI_OPENGL_SUPPORT 1
#endif

#ifndef VSTGUI_TOUCH_EVENT_HANDLING
	#define VSTGUI_TOUCH_EVENT_HANDLING 0
#endif

#ifndef VSTGUI_ENABLE_XML_PARSER
	#define VSTGUI_ENABLE_XML_PARSER 1
#endif

#if VSTGUI_ENABLE_DEPRECATED_METHODS
	#define VSTGUI_OVERRIDE_VMETHOD	override
	#define VSTGUI_FINAL_VMETHOD final
#else
	#define VSTGUI_OVERRIDE_VMETHOD	static_assert (false, "VSTGUI_OVERRIDE_VMETHOD is deprecated, just use override!");
	#define VSTGUI_FINAL_VMETHOD static_assert (false, "VSTGUI_FINAL_VMETHOD is deprecated, just use final!");
#endif

//----------------------------------------------------
// Helper makros
//----------------------------------------------------
#define	VSTGUI_MAKE_STRING_PRIVATE_DONT_USE(x)	# x
#define	VSTGUI_MAKE_STRING(x)					VSTGUI_MAKE_STRING_PRIVATE_DONT_USE(x)

//----------------------------------------------------
#if DEVELOPMENT
	#ifndef DEBUG
		#define DEBUG	1
	#endif
#else
	#if !defined(NDEBUG) && !defined(DEBUG)
		#define NDEBUG	1
	#endif
#endif

//----------------------------------------------------
#define CLASS_METHODS(name, parent) CBaseObject* newCopy () const override { return new name (*this); }
#define CLASS_METHODS_NOCOPY(name, parent) CBaseObject* newCopy () const override { return nullptr; }
#define CLASS_METHODS_VIRTUAL(name, parent) CBaseObject* newCopy () const override = 0;

//----------------------------------------------------
namespace VSTGUI {

/** coordinate type */
using CCoord = double;
/** ID String pointer */
using IdStringPtr = const char*;
/** UTF8 String pointer */
using UTF8StringPtr = const char*;
/** UTF8 String buffer pointer */
using UTF8StringBuffer = char*;

//-----------------------------------------------------------------------------
// @brief Byte Order
//-----------------------------------------------------------------------------
enum ByteOrder
{
	kBigEndianByteOrder = 0,
	kLittleEndianByteOrder,
#if WINDOWS || defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN)
	kNativeByteOrder = kLittleEndianByteOrder
#else
	kNativeByteOrder = kBigEndianByteOrder
#endif
};

//-----------------------------------------------------------------------------
// @brief Message Results
//-----------------------------------------------------------------------------
enum CMessageResult
{
	kMessageUnknown = 0,
	kMessageNotified = 1
};

//-----------------------------------------------------------------------------
class IReference
{
public:
	/** decrease refcount and delete object if refcount == 0 */
	virtual void forget () = 0;
	/** increase refcount */
	virtual void remember () = 0;
};

//-----------------------------------------------------------------------------
template <typename T>
class ReferenceCounted : virtual public IReference
{
public:
	ReferenceCounted () = default;
	virtual ~ReferenceCounted () noexcept = default;

	ReferenceCounted (const ReferenceCounted&) {}
	ReferenceCounted& operator= (const ReferenceCounted&) { return *this; }

	//-----------------------------------------------------------------------------
	/// @name Reference Counting Methods
	//-----------------------------------------------------------------------------
	//@{
	void forget () override { if (--nbReference == 0) { beforeDelete (); delete this; } }
	void remember () override { nbReference++; }
	/** get refcount */
	virtual int32_t getNbReference () const { return nbReference; }
	//@}
private:
	virtual void beforeDelete () {}

	T nbReference {1};
};

using AtomicReferenceCounted = ReferenceCounted<std::atomic<int32_t>>;
using NonAtomicReferenceCounted = ReferenceCounted<int32_t>;

//-----------------------------------------------------------------------------
// CBaseObject Declaration
//! @brief Base Object with reference counter
//-----------------------------------------------------------------------------
class CBaseObject : public NonAtomicReferenceCounted
{
public:
	CBaseObject () = default;
	~CBaseObject () noexcept override = default;

	CBaseObject (const CBaseObject&) {}
	CBaseObject& operator= (const CBaseObject&) { return *this; }

	//-----------------------------------------------------------------------------
	/// @name Message Methods
	//-----------------------------------------------------------------------------
	//@{
	virtual CMessageResult notify ([[maybe_unused]] CBaseObject* sender,
								   [[maybe_unused]] IdStringPtr message)
	{
		return kMessageUnknown;
	}
	//@}

	/// @cond ignore
	virtual CBaseObject* newCopy () const { return nullptr; }
	/// @endcond
};

//------------------------------------------------------------------------
template <class I>
class SharedPointer
{
public:
//------------------------------------------------------------------------
	inline SharedPointer (I* ptr, bool remember = true) noexcept;
	inline SharedPointer (const SharedPointer&) noexcept;
	inline SharedPointer () noexcept;
	inline ~SharedPointer () noexcept;

	inline I* operator=(I* ptr) noexcept;
	inline SharedPointer<I>& operator=(const SharedPointer<I>& ) noexcept;

	inline operator I* ()  const noexcept { return ptr; }      // act as I*
	inline I* operator->() const noexcept { return ptr; }      // act as I*

	inline I* get () const noexcept { return ptr; }

	template<class T> T* cast () const { return dynamic_cast<T*> (ptr); }

	inline SharedPointer (SharedPointer<I>&& mp) noexcept;
	inline SharedPointer<I>& operator=(SharedPointer<I>&& mp) noexcept;

	template<typename T>
	inline SharedPointer (const SharedPointer<T>& op) noexcept
	{
		*this = static_cast<I*> (op.get ());
	}

	template<typename T>
	inline SharedPointer& operator= (const SharedPointer<T>& op) noexcept
	{
		*this = static_cast<I*> (op.get ());
		return *this;
	}

	template<typename T>
	inline SharedPointer (SharedPointer<T>&& op) noexcept
	{
		*this = std::move (op);
	}

	template<typename T>
	inline SharedPointer& operator= (SharedPointer<T>&& op) noexcept
	{
		if (ptr)
			ptr->forget ();
		ptr = op.ptr;
		op.ptr = nullptr;
		return *this;
	}

//------------------------------------------------------------------------
protected:
	template<typename T>
	friend class SharedPointer;

	I* ptr {nullptr};
};

//-----------------------------------------------------------------------------
class CBaseObjectGuard
{
public:
	explicit CBaseObjectGuard (CBaseObject* _obj) : obj (_obj) {}
protected:
	SharedPointer<CBaseObject> obj;
};


//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I>::SharedPointer (I* _ptr, bool remember) noexcept
: ptr (_ptr)
{
	if (ptr && remember)
		ptr->remember ();
}

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I>::SharedPointer (const SharedPointer<I>& other) noexcept
: ptr (other.ptr)
{
	if (ptr)
		ptr->remember ();
}

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I>::SharedPointer () noexcept
: ptr (nullptr)
{}

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I>::~SharedPointer () noexcept
{
	if (ptr)
		ptr->forget ();
}

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I>::SharedPointer (SharedPointer<I>&& mp) noexcept
: ptr (nullptr)
{
	*this = std::move (mp);
}

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I>& SharedPointer<I>::operator=(SharedPointer<I>&& mp) noexcept
{
	if (ptr)
		ptr->forget ();
	ptr = mp.ptr;
	mp.ptr = nullptr;
	return *this;
}

//------------------------------------------------------------------------
template <class I>
inline I* SharedPointer<I>::operator=(I* _ptr) noexcept
{
	if (_ptr != ptr)
	{
		if (ptr)
			ptr->forget ();
		ptr = _ptr;
		if (ptr)
			ptr->remember ();
	}
	return ptr;
}

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I>& SharedPointer<I>::operator=(const SharedPointer<I>& _ptr) noexcept
{
	operator= (_ptr.ptr);
	return *this;
}

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I> owned (I* p) noexcept { return SharedPointer<I> (p, false); }

//------------------------------------------------------------------------
template <class I>
inline SharedPointer<I> shared (I* p) noexcept { return SharedPointer<I> (p, true); }

//------------------------------------------------------------------------
template <class I, typename ...Args>
inline SharedPointer<I> makeOwned (Args&& ...args)
{
	return SharedPointer<I> (new I (std::forward<Args>(args)...), false);
}

#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/** An owning pointer. \deprecated
 *
 *	This class is now deprecated. Please change your code from
 *	@code{.cpp} OwningPointer<MyClass> obj = new MyClass () @endcode
 *	to
 *	@code{.cpp} SharedPointer<MyClass> obj = makeOwned<MyClass> (); @endcode
 */
template <class I>
class OwningPointer : public SharedPointer<I>
{
public:
//------------------------------------------------------------------------
	inline OwningPointer (I* p) : SharedPointer<I> (p, false) {}
	inline OwningPointer (const SharedPointer<I>& p) : SharedPointer<I> (p) {}
	inline OwningPointer (const OwningPointer<I>& p) : SharedPointer<I> (p) {}
	inline OwningPointer () : SharedPointer<I> () {}
	inline I* operator=(I* _ptr)
	{
		if (_ptr != this->ptr)
		{
			if (this->ptr)
				this->ptr->forget ();
			this->ptr = _ptr;
		}
		return this->ptr;
	}
};
#endif

//------------------------------------------------------------------------
template <typename T, typename B>
inline void setBit (T& storage, B bit, bool state)
{
	static_assert (std::is_integral<T>::value, "only works for integral types");
	static_assert (sizeof (T) >= sizeof (B), "bit type is too big");
	if (state)
		storage |= static_cast<T> (bit);
	else
		storage &= ~(static_cast<T> (bit));
}

//------------------------------------------------------------------------
template <typename T, typename B>
inline constexpr bool hasBit (T storage, B bit)
{
	static_assert (std::is_integral<T>::value, "only works for integral types");
	static_assert (sizeof (T) >= sizeof (B), "bit type is too big");
	return (storage & static_cast<T> (bit)) ? true : false;
}

//-----------------------------------------------------------------------------
template <typename T, typename B>
struct BitScopeToggleT
{
	BitScopeToggleT (T& storage, B bit) : storage (storage), bit (bit) { toggle (); }
	~BitScopeToggleT () noexcept { toggle (); }

private:
	void toggle ()
	{
		bool state = hasBit (storage, bit);
		setBit (storage, bit, !state);
	}
	T& storage;
	B bit;
};

//-----------------------------------------------------------------------------
#define VSTGUI_NEWER_THAN(major, minor) \
	(VSTGUI_VERSION > major || (VSTGUI_VERSION_MAJOR == major && VSTGUI_VERSION_MINOR > minor))

#define VSTGUI_NEWER_THAN_4_10 VSTGUI_NEWER_THAN (4, 10)
#define VSTGUI_NEWER_THAN_4_11 VSTGUI_NEWER_THAN (4, 11)
#define VSTGUI_NEWER_THAN_4_12 VSTGUI_NEWER_THAN (4, 12)
#define VSTGUI_NEWER_THAN_4_13 VSTGUI_NEWER_THAN (4, 13)

} // VSTGUI

//-----------------------------------------------------------------------------
#include "vstguidebug.h"
