#ifndef __OPENCL_WRAPPER_CLASS_H__
#define __OPENCL_WRAPPER_CLASS_H__

#ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif //__APPLE__

#include "clErrorClass.h"
#include <string>
#include <iostream>
#include "clClass.h"


//! openclの関数をまとめたクラス.
/*! OpenCLで使用するデバイスなどを用意する. */
class OpenCLWrapperClass
{
//変数とか
private:
	unsigned int        use_device;		            //!< 使用するデバイス番号を指定しておく
	unsigned int		openCL_device_num;          //!< 利用できるOpenCLデバイスの総数
	clClass				*ocl;						//!< OpenCLの情報を色々包含する
	unsigned int		du;							//!< ダミー
protected:
public:

// 関数とか
private:
	        void getOpenCLDeviceNum();
protected:
public:
	OpenCLWrapperClass();
	~OpenCLWrapperClass();

// ラッパクラスの動作
			unsigned int		getUsableOpenCLDeviceNum() const;
			void				setUseDevice(unsigned int num);
	const	unsigned int		getUseDevice() const;

// プラットフォーム
	const	char				*getPlatformInfo(cl_platform_info platform_info) const;
			void				showPlatformInfo(cl_platform_info platform_info) const;
			void				showPlatformInfoAll() const;

// デバイス
	const	cl_device_id		getDeviceID() const;
	const	char				*getDeviceInfo(cl_device_info device_info) const;
			void				showDeviceInfo(cl_device_info device_info) const;
			void				showDeviceInfoAll() const;

// コンテキスト
	const	cl_context			getContext() const;

// プログラム
			void				createProgramObject(const char *filename);

// カーネル
			void				createKernel(const char *file_name ,const char *kernel_name);
	const	cl_kernel			getKernel(std::string kernel_name) const;

#if 1
template<class T1>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 1,
		a1, 	s1, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,   0);
}

template<class T1, class T2>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 2,
		a1, 	s1, 
		a2, 	s2, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 3,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 4,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 5,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 6,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 7,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 8,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8,
T9     *a9,  size_t  s9)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 9,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		a9, 	s9, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8,
T9     *a9,  size_t  s9,
T10    *a10, size_t  s10)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 10,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		a9, 	s9, 
		a10, 	s10, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8,
T9     *a9,  size_t  s9,
T10    *a10, size_t  s10,
T11    *a11, size_t  s11)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 11,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		a9, 	s9, 
		a10, 	s10, 
		a11, 	s11, 
		&du , 	0,
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8,
T9     *a9,  size_t  s9,
T10    *a10, size_t  s10,
T11    *a11, size_t  s11,
T12    *a12, size_t  s12)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 12,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		a9, 	s9, 
		a10, 	s10, 
		a11, 	s11, 
		a12, 	s12, 
		&du , 	0,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8,
T9     *a9,  size_t  s9,
T10    *a10, size_t  s10,
T11    *a11, size_t  s11,
T12    *a12, size_t  s12,
T13    *a13, size_t  s13)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 13,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		a9, 	s9, 
		a10, 	s10, 
		a11, 	s11, 
		a12, 	s12, 
		a13, 	s13,
		&du , 	0,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8,
T9     *a9,  size_t  s9,
T10    *a10, size_t  s10,
T11    *a11, size_t  s11,
T12    *a12, size_t  s12,
T13    *a13, size_t  s13,
T14    *a14, size_t  s14)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 14,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		a9, 	s9, 
		a10, 	s10, 
		a11, 	s11, 
		a12, 	s12, 
		a13, 	s13, 
		a14, 	s14,
		&du ,    0);
}

template<class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15>
void setKernelArgs(std::string kernel_name,
T1     *a1,  size_t  s1,
T2     *a2,  size_t  s2,
T3     *a3,  size_t  s3,
T4     *a4,  size_t  s4,
T5     *a5,  size_t  s5,
T6     *a6,  size_t  s6,
T7     *a7,  size_t  s7,
T8     *a8,  size_t  s8,
T9     *a9,  size_t  s9,
T10    *a10, size_t  s10,
T11    *a11, size_t  s11,
T12    *a12, size_t  s12,
T13    *a13, size_t  s13,
T14    *a14, size_t  s14,
T15    *a15, size_t  s15)
{
	this->ocl[this->use_device].setKernelArgs(kernel_name, 15,
		a1, 	s1, 
		a2, 	s2, 
		a3, 	s3, 
		a4, 	s4,
		a5, 	s5, 
		a6, 	s6, 
		a7, 	s7, 
		a8, 	s8, 
		a9, 	s9, 
		a10, 	s10, 
		a11, 	s11, 
		a12, 	s12, 
		a13, 	s13, 
		a14, 	s14, 
		a15,	s15);
}
#endif

// コマンドキュー
	const	cl_command_queue	getCommandQueue() const;

// メモリー
			void				createMemory(cl_mem_flags flag, size_t memroy_size, void *mem_pointer,std::string mem_name);
	const 	cl_mem              *getMemory(std::string name);

// カーネルの実行とか
			void				runKernel(std::string kernel_name, cl_uint num_dimension, const size_t *offset, size_t *global_work_size,  size_t *local_work_size, cl_uint num_event, const cl_event	*event_list, cl_event *event_ ) const;
			void				runKernel(std::string kernel_name, cl_uint num_dimension, size_t *global_work_size, size_t *local_work_size, cl_uint num_event, const cl_event *event_list, cl_event *event_ ) const;
			void				runKernel(std::string kernel_name, cl_uint num_dimension, const size_t *offset,size_t *global_work_size, size_t *local_work_size) const;
			void				runKernel(std::string kernel_name, cl_uint num_dimension, size_t *global_work_size, size_t *local_work_size) const;

// カーネルの実行結果を取得
			void				getResult(std::string kernel_name, std::string src_memory_name, cl_bool block, size_t offset, size_t memory_size, void *dst_memory, cl_uint num_event, const cl_event *event_list, cl_event *event_ ) const;
			void				getResult(std::string kernel_name, std::string src_memory_name, cl_bool block, size_t memory_size, void *dst_memory, cl_uint num_event, const cl_event *event_list, cl_event *event_ ) const;
			void				getResult(std::string kernel_name, std::string src_memory_name, size_t memory_size, void *dst_memory, cl_uint num_event, const cl_event *event_list, cl_event *event_ ) const;
			void				getResult(std::string kernel_name, std::string src_memory_name, cl_bool block, size_t offset, size_t memory_size, void *dst_memory) const;
			void				getResult(std::string kernel_name, std::string src_memory_name, cl_bool block, size_t memory_size, void *dst_memory) const;
			void				getResult(std::string kernel_name, std::string src_memory_name, size_t memory_size, void *dst_memory) const;
};
#endif //__OPENCL_WRAPPER_CLASS_H__
