#pragma once

#include "../Cuda/ImageView.h"
#include "ScriptTraits.h"

#include <type_traits>

// These type-transforms are used to build concrete arguments
//   for autogenerated cuda-library stubs
namespace CudaLibrary
{
	// Use script defined scalar/vector/image/deferred traits
	template <typename T> using OutParam = Script::OutParam<T>;
	template <typename T> using InParam = Script::InParam<T>;
	template <typename T> using OptParam = Script::OptParam<T>;

	template <typename T> using ImageRef = Script::ImageRef<T>;
	template <typename T> using Image = Script::Image<T>;
	template <typename T> using Vector = Script::Vector<T>;
	template <typename T> using Scalar = Script::Scalar<T>;

	using DeferredType = Script::DeferredType;

	/////////////////////////
	// dtrait_to_concrete_linkage -
	//   Transforms from data-trait type to concrete linkage type (T replaces deferred type)
	//   (e.g. ImageRef<int> -> ImageView<int>)
	/////////////////////////
	template <typename Traits, typename T>
	struct dtrait_to_concrete_link {};

	template <typename BaseType, typename T>
	struct dtrait_to_concrete_link<Scalar<BaseType>,T>
	{
		using type = BaseType;
	};

	template <typename T>
	struct dtrait_to_concrete_link<Scalar<DeferredType>,T>
	{
		using type = T;
	};

	template <typename BaseType, typename T>
	struct dtrait_to_concrete_link<Vector<BaseType>,T>
	{
		using type = Vec<BaseType>;
	};

	template <typename T>
	struct dtrait_to_concrete_link<Vector<DeferredType>,T>
	{
		using type = Vec<T>;
	};

	template <typename BaseType, typename T>
	struct dtrait_to_concrete_link<Image<BaseType>,T>
	{
		using type = ImageView<BaseType>;
	};

	template <typename T>
	struct dtrait_to_concrete_link<Image<DeferredType>,T>
	{
		using type = ImageView<T>;
	};

	template <typename BaseType, typename T>
	struct dtrait_to_concrete_link<ImageRef<BaseType>,T>
	{
		using type = ImageView<BaseType>;
	};

	template <typename T>
	struct dtrait_to_concrete_link<ImageRef<DeferredType>,T>
	{
		using type = ImageView<T>;
	};

	/////////////////////////
	// iotrait_to_concrete -
	//   Transforms from full io-trait types to concrete base types
	//   (e.g. InParam<Image<int>> -> ImageContainer<int>)
	//   NOTE: This leaves deferred types the same as iotrait_to_script
	/////////////////////////
	template <typename Traits, typename OutT, typename InT>
	struct iotrait_to_concrete_link {};

	template <template <typename> class IOTrait, template <typename> class DataTrait,
			  typename BaseType, typename OutT, typename InT>
	struct iotrait_to_concrete_link<IOTrait<DataTrait<BaseType>>,OutT,InT>
	{
		// TODO: Fix script to concrete transform (ImageView/Owner) so we can use const& here
		using type = typename dtrait_to_concrete_link<DataTrait<BaseType>,InT>::type;
	};

	template <template <typename> class DataTrait,
			  typename BaseType, typename OutT, typename InT>
	struct iotrait_to_concrete_link<OutParam<DataTrait<BaseType>>,OutT,InT>
	{
		using type = typename std::add_lvalue_reference<
						typename dtrait_to_concrete_link<DataTrait<BaseType>,OutT>::type
						>::type;
	};

	template <typename Traits, typename OutT, typename InT>
	using link_tfm = typename iotrait_to_concrete_link<Traits,OutT,InT>::type;
};
