/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#pragma once

#include <memory>
#include <QMetaType>
#include <QPointer>
#include <QDebug>

namespace LeechCraft
{
namespace Snails
{
	class ValuedMetaArgument
	{
		struct HolderBase
		{
			virtual int GetTypeId () const = 0;
			virtual const char* GetTypeName () const = 0;
			virtual const void* GetValue () const = 0;

			virtual bool Equals (const HolderBase&) const = 0;

			virtual void DebugPrint (QDebug&) const = 0;
		};

		std::shared_ptr<HolderBase> Holder_;

		template<typename T>
		struct Holder : HolderBase
		{
			const T T_;

			Holder (const T& t)
			: T_ { t }
			{
			}

			int GetTypeId () const
			{
				return qMetaTypeId<T> ();
			}

			const char* GetTypeName () const
			{
				return QMetaType::typeName (qMetaTypeId<T> ());
			}

			const void* GetValue () const
			{
				return static_cast<const void*> (&T_);
			}

			bool Equals (const HolderBase& other) const
			{
				if (GetTypeId () != other.GetTypeId ())
					return false;

				return T_ == static_cast<const Holder<T>&> (other).T_;
			}

			void DebugPrint (QDebug& out) const
			{
				out << T_;
			}

			const T& Unwrap () const
			{
				return T_;
			}
		};
	public:
		ValuedMetaArgument () = default;

		template<typename T>
		ValuedMetaArgument (const T& t)
		: Holder_ { new Holder<T> { t } }
		{
		}

		operator QGenericArgument () const;

		bool operator== (const ValuedMetaArgument& other) const;

		void DebugPrint (QDebug&) const;

		template<typename T>
		T GetAs () const
		{
			const auto concrete = dynamic_cast<Holder<T>*> (Holder_.get ());
			if (!concrete)
			{
				const auto& holdee = *Holder_;
				const auto& str = std::string { "Unexpected type, holder type: " } +
						typeid (holdee).name () +
						"; requested: " +
						typeid (T).name ();
				throw std::runtime_error { str };
			}

			return concrete->Unwrap ();
		}
	};

	bool operator!= (const ValuedMetaArgument&, const ValuedMetaArgument&);
}
}

template<typename T>
QDebug operator<< (QDebug out, const QPointer<T>& ptr)
{
	return out << ptr.data ();
}

template<typename T>
QDebug operator<< (QDebug out, const std::shared_ptr<T>& ptr)
{
	return out << ptr.get ();
}

QDebug operator<< (QDebug, const LeechCraft::Snails::ValuedMetaArgument&);
