#include "clProgramClass.h"

//--------------------------------------------------------
//----------          clProgramClass          ------------
//--------------------------------------------------------
// -------------------------------------------------------

// -------------------------------------------------------
//! \brief  プログラムオブジェクトを格納する変数を用意
//! \param  なし.
//! \return なし.
clProgramClass::clProgramClass()
{
    __CONSTRUCTOR;
}

// -------------------------------------------------------
//! \brief  プログラムオブジェクトの破棄
//! \param  なし.
//! \return なし.
clProgramClass::~clProgramClass()
{
    __DESTRUCTOR;
    for(unsigned int i=0; i < clprogram.size(); ++i){
        clReleaseProgram(clprogram[i]);
    }
}

// -------------------------------------------------------
//! \brief  プログラムオブジェクトの作成
//! \param  filename   プログラムオブジェクトを作成する元になるプログラム.
//! \param  context    関連付けるコンテキスト.
//! \param  device_id  関連つけるデバイスのIDのリスト.
//! \param  device_num IDリストに含まれるデバイスの数.
//! \return なし.
void clProgramClass::createProgramObject(const char         *filename,
                                         const cl_context   context, 
                                         const cl_device_id device_id)
{try{
    cl_int     status;
    const char *source = NULL;


    // ソースコードをロードする
    source = loadProgramSource(filename);

    // プログラムオブジェクトの生成
    this->filename.push_back(filename);
    this->clprogram.push_back(
        clCreateProgramWithSource(context,
                                  1,
                                  (const char **)&source,
                                  NULL,
                                  &status));
    if(this->clprogram.back() == (cl_program)0){
        delete [] source;
        throw clErrorClass(status, "failed to create program object.", __FUNCTION__);
    }
    //プログラムをビルドする
    status = clBuildProgram(this->clprogram.back(),
                            1,
                            &device_id,
                            NULL,
                            NULL,
                            NULL);
    if(status != CL_SUCCESS){
        delete[] source;
        showBuildLog(this->clprogram.back(), device_id);
        throw clErrorClass(status, "failed to build program object.", __FUNCTION__);
    }
    delete [] source;
}
catch(clErrorClass &err){err.showErrMsg();}catch(...){std::cerr << "unknown error." << std::endl;}}

// -------------------------------------------------------
//! \brief  指定したファイルを読み込む
//! \param  filename   プログラムオブジェクトを作成する元になるプログラム.
//! \return なし.
 inline const char *clProgramClass::loadProgramSource(const char *filename) throw(clErrorClass)
{
    FILE *inFile;
    char *source=NULL;
    size_t filesize = 0;

    inFile = fopen(filename ,"r");
    if(inFile == 0){
        perror("fopen");
        fprintf(stderr, "file: %s\n", filename);
        throw clErrorClass("kernel program file open failed.", __FUNCTION__);
    }
    while(fgetc(inFile) != EOF){filesize++;}    // ファイルのサイズのチェック
    fseek(inFile, 0L, SEEK_SET);                // ファイルの先頭へ
    source = new char[filesize+1];              // ファイルのサイズ分のバッファを取得
    fread(source, filesize, 1, inFile);         // ファイルの読み込み
    source[filesize] = 0;                       // 最後にNULL文字を埋め込む
    fclose(inFile);

    return source;
}

// -------------------------------------------------------
//! \brief  ビルドログを表示する
//! \param  program プログラムオブジェクトを作成する元になるプログラム.
//! \param  device  対象のデバイス.
//! \return なし.
inline void clProgramClass::showBuildLog(const cl_program   program,
                                         const cl_device_id device) const throw(clErrorClass)
{
    cl_int status;
    size_t size_ret;

    // ビルドログの文の長さを取得
    status = clGetProgramBuildInfo(program,
                                   device,
                                   CL_PROGRAM_BUILD_LOG,
                                   NULL,
                                   NULL,
                                   &size_ret);
    if(status != CL_SUCCESS){
        throw clErrorClass(status, "clGetProgramInfo get buffer size failed." , __FUNCTION__);
    }

    // ビルドログを取得
    char *buffer = new char[size_ret];
    status = clGetProgramBuildInfo(program,
                                   device,
                                   CL_PROGRAM_BUILD_LOG,
                                   size_ret,
                                   buffer,
                                   NULL);
    if(status != CL_SUCCESS){
        delete [] buffer;
        throw clErrorClass(status, "clGetProgramInfo get buffer failed." , __FUNCTION__);
    }
    buffer[size_ret] = 0;
    std::cout << ">>> build log <<<" << std::endl;
    std::cout << buffer << std::endl;
    std::cout << ">>> end of build log <<<" << std::endl;

    DBG(press enter);
    NL(1);
    delete [] buffer;
}

// -------------------------------------------------------
//! \brief  ファイル名からプログラムオブジェクトを取得する.
//! \param  program_index プログラムの参照番号.
//! \return プログラムオブジェクトを返却.0
const cl_program clProgramClass::getProgram(const char *filename) const
{
    return this->clprogram[getProgramIndex(filename)];
}

// -------------------------------------------------------
//! \brief  ファイル名からプログラムオブジェクトの参照番号を取得する.
//! \param  filename ファイル名.
//! \return プログラムオブジェクトの参照番号を返却.
inline const unsigned int clProgramClass::getProgramIndex(const char *filename) const
{
    unsigned int index=0;
    for(index=0; index<this->filename.size(); ++index){
        if(this->filename[index] == filename){
            goto LOOP_SUCCESS;
        }
    }
    std::cerr << "file :\"" << filename << "\" not found." << std::endl;
	index=0;
LOOP_SUCCESS:
    return index;
}
