/*======================================================================================

	Program:		Range Imaging Toolkit (RITK)
	Module:			OpenCLContext.h
	Version:		TODO
	Date:			TODO

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
    University Erlangen-Nuremberg, Germany
    All rights reserved.

	This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
	See RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

========================================================================================*/

#ifndef OPENCLCONTEXT_H__
#define OPENCLCONTEXT_H__

#include "RITKOpenCL.h"
#include "CL/cl.h"
#include <QMutex>

namespace ritk
{


	
	

class OPENCL_EXPORT OpenCLContext
{

public:
	/// Singleton access; this is a workaround for singleton without deinitializer as due to ritk singleton problems a deinitialization would crash the program
	static inline OpenCLContext& GetInstance()
	{
		
		//static OpenCLContext *Instance;
		//if(Instance == NULL) Instance = new OpenCLContext();
		//return *Instance;

		static OpenCLContext Instance;
		return Instance;
	}

	

	cl_context getContext(){ return m_clContext; }
	cl_command_queue getCommandQueue(){ return m_clCommandQueue; }

	/// OpenCL functions
	/// all of these functions have no error number returned as they are already internally checked for errors.
	cl_mem oclCreateBuffer(cl_mem_flags flags, size_t size, void *host_ptr);
	cl_mem oclCreateImage2D(cl_mem_flags flags, const cl_image_format *image_format, size_t image_width, size_t image_height, size_t image_row_pitch, void *host_ptr);
	void oclReleaseMemObject (cl_mem memobj);
	void *oclEnqueueMapBuffer (cl_mem buffer, cl_bool blocking_map, cl_map_flags map_flags, size_t offset, size_t cb, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueUnmapMemObject (cl_mem memobj, void *mapped_ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueReadBuffer(cl_mem buffer, cl_bool blocking_read, size_t offset, size_t cb, void *ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueWriteBuffer(cl_mem buffer, cl_bool blocking_write, size_t offset, size_t cb, const void *ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueCopyBuffer(cl_mem src_buffer, cl_mem dst_buffer, size_t src_offset, size_t dst_offset, size_t cb, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueWriteImage(cl_mem image, cl_bool blocking_write, const size_t origin[3], const size_t region[3], size_t input_row_pitch, size_t input_slice_pitch, const void * ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueReadImage(cl_mem image, cl_bool blocking_read, const size_t origin[3], const size_t region[3], size_t row_pitch, size_t slice_pitch, void *ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueCopyImage(cl_mem src_image, cl_mem dst_image, const size_t src_origin[3], const size_t dst_origin[3], const size_t region[3], cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueCopyImageToBuffer(cl_mem src_image, cl_mem dst_buffer, const size_t src_origin[3], const size_t region[3], size_t dst_offset, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclEnqueueCopyBufferToImage(cl_mem src_buffer, cl_mem dst_image, size_t src_offset, const size_t dst_origin[3], const size_t region[3], cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclWaitForEvents(cl_uint num_events, const cl_event *event_list);
	cl_program oclCreateProgramWithSource(const char *programString, const size_t programStringLength);
	void oclBuildProgram(cl_program program, const char *options);
	void oclReleaseProgram(cl_program program);
	cl_kernel oclCreateKernel(cl_program program, const char *kernel_name);
	void oclReleaseKernel(cl_kernel kernel);
	void oclSetKernelArg(cl_kernel kernel, cl_uint arg_index, size_t arg_size, const void *arg_value);
	void oclEnqueueNDRangeKernel(cl_kernel kernel, cl_uint work_dim, const size_t *global_work_offset, const size_t *global_work_size, const size_t *local_work_size, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event);
	void oclFinish();
	cl_sampler oclCreateSampler(cl_bool normalized_coords, cl_addressing_mode addressing_mode, cl_filter_mode filter_mode);
	void oclReleaseSampler(cl_sampler sampler);
	/// this function is a blocking call until event happens
	cl_int oclGetEventReturnValue(cl_event event);

protected:
	/// OpenCL device
	cl_device_id m_clDevice;
	/// OpenCL context
	cl_context m_clContext;
	/// OpenCL command queue
	cl_command_queue m_clCommandQueue;
	/// Mutex for OpenCL functions' synchronization;
	QMutex m_mutex;

	
private:
	/// Private constructor due to the singleton design pattern
	OpenCLContext();
	/// Private destrutor due to the singleton desing pattern
	~OpenCLContext();

private:
	int numMemObjs;
	int numProgs;
	int numKernels;
	int numMaps;


}; // end class OpenClContext








} // end namespace ritk


#endif // OPENCLCONTEXT_H__