#include "clClass.h"

//--------------------------------------------------------
//----------              clClass             ------------
//--------------------------------------------------------
// -------------------------------------------------------

// -------------------------------------------------------
//! \brief  OpenCLを扱うのに必要なモノを用意する.
//! \param  なし.
//! \return なし.
clClass::clClass()
{
    __CONSTRUCTOR;
}

// -------------------------------------------------------
//! \brief  OpenCLを扱うのに必要なモノを用意する.
//! \param  なし.
//! \return なし.
void clClass::set(cl_device_id device_id)
{try{
    __CONSTRUCTOR;

    cl_int status;
    this->device_id = device_id;
    // device_idに関連付けられているプラットフォームを取得
    status = clGetDeviceInfo(this->device_id,
                             CL_DEVICE_PLATFORM,
                             sizeof(cl_platform_id),
                             &this->platform_id,    // platform_idを取得
                             NULL);
    if(status != CL_SUCCESS){
        throw clErrorClass(status, "get platformID failed.", __FUNCTION__);
    }

    // コンテキストを取得
    this->context = clCreateContext(0,
                                    1,
                                    &this->device_id,
                                    NULL,
                                    NULL,
                                    &status);
    if(status != CL_SUCCESS){
        throw clErrorClass(status, "clCreateContextInfo failed.", __FUNCTION__);
    }

    // コマンドキューを取得
    this->command_queue = clCreateCommandQueue(this->context,
                                               this->device_id,
                                               NULL,
                                               &status);
    if(status != CL_SUCCESS){
        throw clErrorClass(status, "failed to create command queue.", __FUNCTION__);
    }

    this->C_platform     = new clPlatformClass(this->platform_id);
    this->C_device       = new clDeviceClass(this->device_id);
//    this->C_context      = new clContextClass(this->device_id);
//    this->C_commandqueue = new clCommandQueueClass(this->context, this->device_id);
    this->C_program      = new clProgramClass();
    this->C_kernel       = new clKernelClass();
    this->C_memory       = new clMemoryClass();
}catch(clErrorClass &err){err.showErrMsg();exit(1);}catch(...){std::cerr << "unknown error." << std::endl;}}

// -------------------------------------------------------
//! \brief  使用した変数などを破棄する.
//! \param  なし.
//! \return なし.
clClass::~clClass()
{
    __DESTRUCTOR;
    delete this->C_platform;
    delete this->C_device;
//    delete this->C_context;
//    delete this->C_commandqueue;
    delete this->C_program;
    delete this->C_kernel;
    delete this->C_memory;

    clReleaseContext(context);
    clReleaseCommandQueue(command_queue);
}












// プラットフォーム関連
// -------------------------------------------------------
//! \brief  プラットフォームのIDを取得する.
//! \param  platform_index プラットフォームの参照番号.
//! \return プラットフォームのIDを返却する.
const cl_platform_id clClass::getPlatformId() const
{
    return this->platform_id;
}

// -------------------------------------------------------
//! \brief  プラットフォームの参照番号とプラットフォームの知りたい情報から詳細情報を取得する.
//! \param  platform_index プラットフォームの参照番号.
//! \param  platform_info  知りたい情報.
//! \return プラットフォームの詳細情報を返却する.
const char *clClass::getPlatformInfo(cl_platform_info platform_info) const
{
    return this->C_platform->getPlatformInfo(platform_info);
}

// -------------------------------------------------------
//! \brief  プラットフォームの参照番号とプラットフォームの知りたい情報から詳細情報を表示する.
//! \param  platform_index プラットフォームの参照番号.
//! \param  platform_info  知りたい情報.
//! \return なし.
void clClass::showPlatformInfo(cl_platform_info platform_info) const
{
    this->C_platform->showPlatformInfo(platform_info);
}

// -------------------------------------------------------
//! \brief  プラットフォームの参照番号から詳細情報をすべて表示する.
//! \param  platform_index プラットフォームの参照番号.
//! \return なし.
void clClass::showPlatformInfoAll() const
{
    this->C_platform->showPlatformInfoAll();
}







// デバイス関連
// -------------------------------------------------------
//! \brief  デバイスのIDを取得する.
//! \param  なし.
//! \return デバイスのIDを返却する.
const cl_device_id clClass::getDeviceId() const
{
    return this->device_id;
}

// -------------------------------------------------------
//! \brief  デバイスの詳細情報を取得する.
//! \param  platform_info  知りたい情報.
//! \return デバイスの詳細情報を返却する.
const char *clClass::getDeviceInfo(cl_device_info device_info ) const
{
    return this->C_device->getDeviceInfo(device_info);
}

// -------------------------------------------------------
//! \brief  デバイスの情報を指定して表示する.
//! \param  device_info    知りたいデバイスの情報.
//! \return なし.
void clClass::showDeviceInfo(cl_device_info device_info) const
{
    this->C_device->showDeviceInfo(device_info);
}

// -------------------------------------------------------
//! \brief  デバイスの詳細情報を表示する.
//! \param  なし.
//! \return なし.
void clClass::showDeviceInfoAll() const
{
    this->C_device->showDeviceInfoAll();
}








//コンテキスト関連
// -------------------------------------------------------
//! \brief  コンテキストを取得する.
//! \param  なし.
//! \return コンテキストを返却する.
const cl_context clClass::getContext() const
{
    return this->context;
}






//コマンドキュー関連
// -------------------------------------------------------
//! \brief  コマンドキューを取得する.
//! \param  なし.
//! \return コマンドキューを返却する.
const cl_command_queue clClass::getCommandQueue() const
{
    return this->command_queue;
}






//プログラム関連
// -------------------------------------------------------
//! \brief  ファイル名を指定してプログラムオブジェクトを作成する.
/*! */
//! \param  filename ファイル名.
//! \return なし.
void clClass::createProgramObject(const char *filename)
{
    this->C_program->createProgramObject(filename, 
                                         this->context,
                                         this->device_id);
}






//カーネル関連
// -------------------------------------------------------
//! \brief  ファイル名を指定してプログラムオブジェクトを作成する.
/*! */
//! \param  filename ファイル名.
//! \return なし.
void clClass::createKernel(const char *file_name ,const char *kernel_name)
{
    this->C_kernel->createKernel(this->C_program->getProgram(file_name),
                                 kernel_name);
}

// -------------------------------------------------------
//! \brief  カーネルを返却する.
//! \param  なし.
//! \return カーネルを返却する.
const cl_kernel clClass::getKernel(std::string kernel_name) const
{
    return this->C_kernel->getKernel(kernel_name);
}

// -------------------------------------------------------
//! \brief  カーネルの参照を取得.
//! \param  カーネルの名前.
//! \return カーネルの参照を返却する.
const void clClass::setKernelArg(std::string  kernel_name,
                                 unsigned int arg_index,
                                 size_t       memory_size,
                                 std::string  memory_name) throw(clErrorClass)
{
    this->C_kernel->setKernelArgs(kernel_name,
                                  C_memory->getMemory(memory_name),
                                  arg_index,
                                  memory_size);
}

// -------------------------------------------------------
//! \brief  カーネルの参照を取得.
//! \param  カーネルの名前.
//! \return カーネルの参照を返却する.
const void clClass::setKernelArg(std::string  kernel_name,
                                 unsigned int arg_index,
                                 size_t       memory_size,
                                 void *memory) throw(clErrorClass)
{
    this->C_kernel->setKernelArgs(kernel_name,
                                  memory,
                                  arg_index,
                                  memory_size);
}





//メモリ関連
// -------------------------------------------------------
//! \brief  ファイル名を指定してプログラムオブジェクトを作成する.
//! \param  filename ファイル名.
//! \return なし.
const void clClass::createMemory(cl_mem_flags flag, size_t memroy_size, void *mem_pointer, std::string name) const
{
    C_memory->createMemory(this->context,
                           flag,
                           memroy_size,
                           mem_pointer,
                           name);
}

cl_mem *clClass::getMemory(unsigned int index) const
{
    return C_memory->getMemory(index);
}

cl_mem *clClass::getMemory(std::string name) const
{
    return C_memory->getMemory(name);
}






// カーネルの実行とか
// -------------------------------------------------------
//! \brief  ファイル名を指定してプログラムオブジェクトを作成する.
//! \param  filename ファイル名.
//! \return なし.
void clClass::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
{
    cl_uint status;
    status = clEnqueueNDRangeKernel(this->command_queue,                    // コマンドキュー
                                    this->C_kernel->getKernel(kernel_name), // 実行カーネル名
                                    num_dimension,                          // 次元数
                                    offset,                                 // グローバルワークスペースのオフセット=0
                                    global_work_size,                       // グローバルのワークスペースサイズ
                                    local_work_size,                        // ワークグループのサイズ
                                    num_event,                              // イベントの数
                                    event_list,                             // イベントのリスト
                                    event_);                                // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueNDRangeKernel failed.", __FUNCTION__);
        err.showErrMsg();
    }
}

void clClass::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
{
    cl_uint status;
    status = clEnqueueNDRangeKernel(this->command_queue,                    // コマンドキュー
                                    this->C_kernel->getKernel(kernel_name), // 実行カーネル名
                                    num_dimension,                          // 次元数
                                    0,                                      // グローバルワークスペースのオフセット=0
                                    global_work_size,                       // グローバルのワークスペースサイズ
                                    local_work_size,                        // ワークグループのサイズ
                                    num_event,                              // イベントの数
                                    event_list,                             // イベントのリスト
                                    event_);                                // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueNDRangeKernel failed.", __FUNCTION__);
        err.showErrMsg();
    }
}

void clClass::runKernel(std::string   kernel_name, 
                        cl_uint       num_dimension, 
                        const size_t  *offset,
                        size_t        *global_work_size, 
                        size_t        *local_work_size) const
{
    cl_uint status;
    status = clEnqueueNDRangeKernel(this->command_queue,                    // コマンドキュー
                                    this->C_kernel->getKernel(kernel_name), // 実行カーネル名
                                    num_dimension,                          // 次元数
                                    offset,                                 // グローバルワークスペースのオフセット=0
                                    global_work_size,                       // グローバルのワークスペースサイズ
                                    local_work_size,                        // ワークグループのサイズ
                                    0,                                      // イベントの数
                                    NULL,                                   // イベントのリスト
                                    NULL);                                  // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueNDRangeKernel failed.", __FUNCTION__);
        err.showErrMsg();
    }
}

void clClass::runKernel(std::string  kernel_name, 
                        cl_uint      num_dimension, 
                        size_t       *global_work_size, 
                        size_t       *local_work_size) const
{
    cl_uint status;
    status = clEnqueueNDRangeKernel(this->command_queue,                    // コマンドキュー
                                    this->C_kernel->getKernel(kernel_name), // 実行カーネル名
                                    num_dimension,                          // 次元数
                                    0,                                      // グローバルワークスペースのオフセット=0
                                    global_work_size,                       // グローバルのワークスペースサイズ
                                    local_work_size,                        // ワークグループのサイズ
                                    0,                                      // イベントの数
                                    NULL,                                   // イベントのリスト
                                    NULL);                                  // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueNDRangeKernel failed.", __FUNCTION__);
        err.showErrMsg();
    }
}



// カーネルの実行結果を取得する
// 実行結果を取得する
void clClass::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
{
	cl_int status;
    status = clEnqueueReadBuffer(this->command_queue,
								 *this->C_memory->getMemory(src_memory_name), // 受け取る結果が格納されているメモリオブジェクト(src)
                                 block,                                             // ブロッキングするかしないか
                                 offset,                                            // 読み込みデータのオフセット
                                 memory_size,                                       // 読み込むデータのサイズ
                                 dst_memory,                                        // 結果を受け取る配列(dst)
                                 num_event,                                         // イベントの数
                                 event_list,                                        // イベントのリスト
                                 event_);                                           // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueReadBuffer failed.", __FUNCTION__);
        err.showErrMsg();
    }
}


void clClass::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
{
	cl_int status;
    status = clEnqueueReadBuffer(this->command_queue,
	                             *this->C_memory->getMemory(src_memory_name),       // 受け取る結果が格納されているメモリオブジェクト(src)
                                 block,                                             // ブロッキングするかしないか
                                 0,                                                 // 読み込みデータのオフセット
                                 memory_size,                                       // 読み込むデータのサイズ
                                 dst_memory,                                        // 結果を受け取る配列(dst)
                                 num_event,                                         // イベントの数
                                 event_list,                                        // イベントのリスト
                                 event_);                                           // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueReadBuffer failed.", __FUNCTION__);
        err.showErrMsg();
    }
}


void clClass::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
{
	cl_int status;
    status = clEnqueueReadBuffer(this->command_queue,
	                             *this->C_memory->getMemory(src_memory_name),       // 受け取る結果が格納されているメモリオブジェクト(src)
                                 CL_TRUE,                                             // ブロッキングするかしないか
                                 0,                                            // 読み込みデータのオフセット
                                 memory_size,                                       // 読み込むデータのサイズ
                                 dst_memory,                                        // 結果を受け取る配列(dst)
                                 num_event,                                         // イベントの数
                                 event_list,                                        // イベントのリスト
                                 event_);                                           // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueReadBuffer failed.", __FUNCTION__);
        err.showErrMsg();
    }
}


void clClass::getResult(std::string        kernel_name, 
	                    std::string        src_memory_name,
						cl_bool            block,
						size_t             offset,
						size_t             memory_size,
						void               *dst_memory) const
{
	cl_int status;
    status = clEnqueueReadBuffer(this->command_queue,
								 *this->C_memory->getMemory(src_memory_name), // 受け取る結果が格納されているメモリオブジェクト(src)
                                 block,                                             // ブロッキングするかしないか
                                 offset,                                            // 読み込みデータのオフセット
                                 memory_size,                                       // 読み込むデータのサイズ
                                 dst_memory,                                        // 結果を受け取る配列(dst)
                                 0,                                                 // イベントの数
                                 NULL,                                              // イベントのリスト
                                 NULL);                                             // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueReadBuffer failed.", __FUNCTION__);
        err.showErrMsg();
    }
}


void clClass::getResult(std::string        kernel_name, 
	                    std::string        src_memory_name,
						cl_bool            block,
						size_t             memory_size,
						void               *dst_memory) const
{
	cl_int status;
    status = clEnqueueReadBuffer(this->command_queue,
	                             *this->C_memory->getMemory(src_memory_name),       // 受け取る結果が格納されているメモリオブジェクト(src)
                                 block,                                             // ブロッキングするかしないか
                                 0,                                                 // 読み込みデータのオフセット
                                 memory_size,                                       // 読み込むデータのサイズ
                                 dst_memory,                                        // 結果を受け取る配列(dst)
                                 0,                                                 // イベントの数
                                 NULL,                                              // イベントのリスト
                                 NULL);                                             // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueReadBuffer failed.", __FUNCTION__);
        err.showErrMsg();
    }
}


void clClass::getResult(std::string        kernel_name, 
	                    std::string        src_memory_name,
						size_t             memory_size,
						void               *dst_memory) const
{
	cl_int status;
    status = clEnqueueReadBuffer(this->command_queue,
	                             *this->C_memory->getMemory(src_memory_name),       // 受け取る結果が格納されているメモリオブジェクト(src)
                                 CL_TRUE,                                           // ブロッキングするかしないか
                                 0,                                                 // 読み込みデータのオフセット
                                 memory_size,                                       // 読み込むデータのサイズ
                                 dst_memory,                                        // 結果を受け取る配列(dst)
                                 0,                                                 // イベントの数
                                 NULL,                                              // イベントのリスト
                                 NULL);                                             // イベントへのポインタ
    if(status != CL_SUCCESS){
        clErrorClass err(status, "clEnqueueReadBuffer failed.", __FUNCTION__);
        err.showErrMsg();
    }
}
