#include "clDeviceClass.h"

//--------------------------------------------------------
//----------           clDeviceClass          ------------
//--------------------------------------------------------
// -------------------------------------------------------

// -------------------------------------------------------
//! \brief  コンストラクタ. デバイスを用意する関数を呼び出す.
//! \param  platform_id プラットフォームのID.
//! \return なし.
clDeviceClass::clDeviceClass(cl_device_id device_id):
    device_id(device_id)                 //デバイスのID
{
    __CONSTRUCTOR;
	this->setDeviceInfo();
}

// -------------------------------------------------------
//! \brief  デストラクタ. デバイスを破棄する.
//! \param  なし.
//! \return なし.
clDeviceClass::~clDeviceClass()
{
    __DESTRUCTOR;
    for(unsigned int i=0; i<num_info; ++i){
        delete [] any[i];
    }
    delete [] any;
	any = NULL;
}

// -------------------------------------------------------
//! \brief  デバイスの情報をすべて表示する.
//! \param  device_index デバイスIDが格納されている配列の参照.
//! \param  device_info  知りたいデバイスの情報.
//! \return なし.
inline void clDeviceClass::showDeviceInfo(cl_device_info device_info) const throw(clErrorClass)
{
    std::string str  = "";
    std::stringstream str2;

    const void *vp = this->getDeviceInfo(device_info);
    switch(device_info){
    case CL_DEVICE_TYPE:
        str = "type                          : ";
        switch(*(cl_device_type*)vp){
        case CL_DEVICE_TYPE_CPU:
            str2 << "CPU";
            break;
        case CL_DEVICE_TYPE_GPU:
            str2 << "GPU";
            break;
        case CL_DEVICE_TYPE_ACCELERATOR:
            str2 << "ACCELERATOR";
            break;
        default:
            str2 << "DEFAULT";
            break;
        }
        break;
    case CL_DEVICE_VENDOR_ID:
        str = "vendor id                     : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MAX_COMPUTE_UNITS:
        str = "max compute units             : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS:
        str = "nax work item dimensions      : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MAX_WORK_GROUP_SIZE:
        str = "max work group size           : ";    
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_MAX_WORK_ITEM_SIZES:
        str = "max work item size            : ";    
        str2 << "(" << ((size_t*)vp)[0] <<  ", " << ((size_t*)vp)[1] << ", " << ((size_t*)vp)[2] << ")" ;
        break;
    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR:
        str = "preferred vector width char   : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT:
        str = "preferred vector width short  : ";    
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT:
        str = "preferred vector width int    : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG:
        str = "preferred vector width long   : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT:
        str = "preferred vector width float  : ";    
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE:
        str = "preferred vector width double : ";    
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MAX_CLOCK_FREQUENCY:
        str = "max clock frequency           : ";    
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_ADDRESS_BITS:
        str = "address bits                  : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MAX_READ_IMAGE_ARGS:
        str = "max read image args           : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MAX_WRITE_IMAGE_ARGS:
        str = "max write image args          : ";    
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MAX_MEM_ALLOC_SIZE:
        str = "max memory alloc size         : ";
        str2 << *(cl_ulong*)vp;
        break;
    case CL_DEVICE_IMAGE2D_MAX_WIDTH:
        str = "image 2D max width            : ";
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_IMAGE2D_MAX_HEIGHT:
        str = "image 3D max height           : ";
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_IMAGE3D_MAX_WIDTH:
        str = "image 3D max width            : ";
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_IMAGE3D_MAX_HEIGHT:
        str = "image 3D max height           : ";
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_IMAGE3D_MAX_DEPTH:
        str = "image 3D max depth            : ";
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_IMAGE_SUPPORT:
        str = "image support                 : ";
        if(*(cl_bool*)vp){
            str2 << "True";
        }else{
            str2 << "False";
        }
        break;
    case CL_DEVICE_MAX_PARAMETER_SIZE:
        str = "max parameter size            : ";
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_MAX_SAMPLERS:
        str = "max samplers                  : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MEM_BASE_ADDR_ALIGN:
        str = "memory base address align     : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE:
        str = "min data type align size      : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_SINGLE_FP_CONFIG:
        str = "single fp config              : ";
        str2 << *(cl_device_fp_config*)vp;
        break;
    case CL_DEVICE_GLOBAL_MEM_CACHE_TYPE:
        str = "global memory cache type      : ";
        str2 << *(cl_device_mem_cache_type*)vp;
        switch(*(cl_device_mem_cache_type*)vp){
        case CL_NONE:
            str2 << "(NONE)";
            break;
        case CL_READ_ONLY_CACHE:
            str2 << "(READ_ONLY_CACHE)";
            break;
        case CL_READ_WRITE_CACHE:
            str2 << "(READ_WRITE_CACHE)";
            break;
        default:
            break;
        }
        break;
    case CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE:
        str = "global memory cacheline size  : ";
        str2 << *(cl_ulong*)vp;
        break;
    case CL_DEVICE_GLOBAL_MEM_CACHE_SIZE:
        str = "global memory cache size      : ";
        str2 << *(cl_ulong*)vp;
        break;
    case CL_DEVICE_GLOBAL_MEM_SIZE:
        str = "global memory size            : ";
        str2 << *(cl_ulong*)vp;
        break;
    case CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE:
        str = "max constant buffer size      : ";
        str2 << *(cl_ulong*)vp;
        break;
    case CL_DEVICE_MAX_CONSTANT_ARGS:
        str = "max constant args             : ";
        str2 << *(cl_uint*)vp;
        break;
    case CL_DEVICE_LOCAL_MEM_TYPE:
        str = "local memory type             : ";
        str2 << *(cl_device_local_mem_type*)vp;
        break;
    case CL_DEVICE_LOCAL_MEM_SIZE:
        str = "local memory size             : ";
        str2 << *(cl_ulong*)vp;
        break;
    case CL_DEVICE_ERROR_CORRECTION_SUPPORT:
        str = "error correction support      : ";
        if(*(cl_bool*)vp){
            str2 << "True";
        }else{
            str2 << "False";
        }
        break;
    case CL_DEVICE_PROFILING_TIMER_RESOLUTION:
        str = "profiling timer resolution    : ";
        str2 << *(size_t*)vp;
        break;
    case CL_DEVICE_ENDIAN_LITTLE:
        str = "endian little                 : ";
        if(*(cl_bool*)vp){
            str2 << "True";
        }else{
            str2 << "False";
        }
        break;
    case CL_DEVICE_AVAILABLE:
        str = "available                     : ";
        if(*(cl_bool*)vp){
            str2 << "True";
        }else{
            str2 << "False";
        }
        break;
    case CL_DEVICE_COMPILER_AVAILABLE:
        str = "compiler available            : ";
        if(*(cl_bool*)vp){
            str2 << "True";
        }else{
            str2 << "False";
        }
        break;
    case CL_DEVICE_EXECUTION_CAPABILITIES:
        str = "execution capabilities        : ";
        str2 << *(cl_device_exec_capabilities*)vp;
        break;
    case CL_DEVICE_QUEUE_PROPERTIES:
        str = "queue properties              : ";
        str2 << *(cl_command_queue_properties*)vp;
        break;
    case CL_DEVICE_NAME:
        str = "name                          : ";
        str2 << (char*)vp;
        break;
    case CL_DEVICE_VENDOR:
        str = "vendor                        : ";
        str2 << (char*)vp;
        break;
    case CL_DRIVER_VERSION:
        str = "driver version                : ";
        str2 << (char*)vp;
        break;
    case CL_DEVICE_PROFILE:
        str = "profile                       : ";
        str2 << (char*)vp;
        break;
    case CL_DEVICE_VERSION:
        str = "version                       : ";
        str2 << (char*)vp;
        break;
    case CL_DEVICE_EXTENSIONS:
        str = "extensions                    : ";
        str2 << (char*)vp;
        break;
    case CL_DEVICE_PLATFORM:
        str = "platform                      : ";
        str2 << *(cl_platform_id*)vp;
        break;
    default:
        break;
    }
    std::cout << "Device " << str << str2.str() << std::endl;
}

// -------------------------------------------------------
//! \brief  デバイスの情報をすべて表示する.
//! \param  device_index デバイスIDが格納されている配列の参照.
//! \return なし.
void clDeviceClass::showDeviceInfoAll() const
{
    std::cout << "Device ID                            : " << device_id << std::endl;
	for(unsigned int i=0; i < this->num_info; ++i){
		showDeviceInfo(this->deviceinfo[i]);
    }
}

// -------------------------------------------------------
//! \brief  デバイスの情報を格納する変数などの開放.
//! \param  device_id 情報を取得するデバイスのID.
//! \return なし.
inline void clDeviceClass::setDeviceInfo()
{try{
    const static cl_device_info tmp[] ={
         CL_DEVICE_TYPE                              
        ,CL_DEVICE_VENDOR_ID                         
        ,CL_DEVICE_MAX_COMPUTE_UNITS                 
        ,CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS          
        ,CL_DEVICE_MAX_WORK_GROUP_SIZE               
        ,CL_DEVICE_MAX_WORK_ITEM_SIZES               
        ,CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR       
        ,CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT      
        ,CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT        
        ,CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG       
        ,CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT      
        ,CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE     
        ,CL_DEVICE_MAX_CLOCK_FREQUENCY               
        ,CL_DEVICE_ADDRESS_BITS                      
        ,CL_DEVICE_MAX_READ_IMAGE_ARGS               
        ,CL_DEVICE_MAX_WRITE_IMAGE_ARGS              
        ,CL_DEVICE_MAX_MEM_ALLOC_SIZE                
        ,CL_DEVICE_IMAGE2D_MAX_WIDTH                 
        ,CL_DEVICE_IMAGE2D_MAX_HEIGHT                
        ,CL_DEVICE_IMAGE3D_MAX_WIDTH                 
        ,CL_DEVICE_IMAGE3D_MAX_HEIGHT                
        ,CL_DEVICE_IMAGE3D_MAX_DEPTH                 
        ,CL_DEVICE_IMAGE_SUPPORT                     
        ,CL_DEVICE_MAX_PARAMETER_SIZE                
        ,CL_DEVICE_MAX_SAMPLERS                      
        ,CL_DEVICE_MEM_BASE_ADDR_ALIGN               
        ,CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE          
        ,CL_DEVICE_SINGLE_FP_CONFIG                  
        ,CL_DEVICE_GLOBAL_MEM_CACHE_TYPE             
        ,CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE         
        ,CL_DEVICE_GLOBAL_MEM_CACHE_SIZE             
        ,CL_DEVICE_GLOBAL_MEM_SIZE                   
        ,CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE          
        ,CL_DEVICE_MAX_CONSTANT_ARGS                 
        ,CL_DEVICE_LOCAL_MEM_TYPE                    
        ,CL_DEVICE_LOCAL_MEM_SIZE                    
        ,CL_DEVICE_ERROR_CORRECTION_SUPPORT          
        ,CL_DEVICE_PROFILING_TIMER_RESOLUTION        
        ,CL_DEVICE_ENDIAN_LITTLE                     
        ,CL_DEVICE_AVAILABLE                         
        ,CL_DEVICE_COMPILER_AVAILABLE                
        ,CL_DEVICE_EXECUTION_CAPABILITIES            
        ,CL_DEVICE_QUEUE_PROPERTIES                  
        ,CL_DEVICE_NAME                              
        ,CL_DEVICE_VENDOR                            
        ,CL_DRIVER_VERSION                           
        ,CL_DEVICE_PROFILE                           
        ,CL_DEVICE_VERSION                           
        ,CL_DEVICE_EXTENSIONS                        
        ,CL_DEVICE_PLATFORM                          
// バージョン1.1以上の情報
#ifdef CL_VERSION_1_1
        ,CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF
        ,CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR
        ,CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT
        ,CL_DEVICE_NATIVE_VECTOR_WIDTH_INT
        ,CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG
        ,CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT
        ,CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE
        ,CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF
        ,CL_DEVICE_HOST_UNIFIED_MEMORY
        ,CL_DEVICE_OPENCL_C_VERSION
#endif
	};

    this->num_info                = sizeof(tmp)/sizeof(*tmp);    //情報の数
    this->deviceinfo              = tmp;
    this->any                     = new __int8*[this->num_info];

    cl_int status = 0;    // エラー判定用
    size_t value_size;    // 配列のサイズを入れる変数
    for(unsigned int i=0; i<this->num_info ; ++i){
         try{
		    // 取得しようとする情報のサイズを得る
			status = clGetDeviceInfo(device_id,
									 this->deviceinfo[i],
									 NULL,
									 NULL,
									 &value_size);
			if(status != CL_SUCCESS){
				std::cout << "for loop " << i << std::endl;
				throw clErrorClass(status, "clGetDeviceInfo get buffer size failed.", __FUNCTION__);
			}
		}
		catch(clErrorClass &err){
			err.showErrMsg();
		}
		catch(...){
			std::cerr << "fatal error." << std::endl;
		}

        try{
            //1Byte * value_size分だけ配列を確保
            this->any[i] = new __int8[value_size];    
            // 情報を取得する
            status = clGetDeviceInfo(device_id,
                                     this->deviceinfo[i],
                                     value_size,
                                     this->any[i],
                                     NULL);
            if(status != CL_SUCCESS){
               throw clErrorClass(status, "clGetDeviceInfo failed.", __FUNCTION__);
            }
        }
		catch(clErrorClass &err){
			err.showErrMsg();
		}
		catch(...){
			std::cerr << "fatal error." << std::endl;
		}
    }
}catch(clErrorClass &err){err.showErrMsg();}catch(...){std::cerr << "unknown error." << std::endl;}}

// -------------------------------------------------------
//! \brief  取得するデバイスの情報を指定してデバイスの情報を返す関数.
/*! デバイスの情報が不正な値なら例外をスローする*/
//! \param  device_info 取得する情報.
//! \return デバイスの情報を返却.
const __int8 *clDeviceClass::getDeviceInfo(cl_device_info device_info) const
{
    unsigned int index=0;
    for(index=0; index<this->num_info; ++index){
        if(this->deviceinfo[index] == device_info){
            goto LOOP_SUCCESS;
        }
    }
    std::cerr << __FUNCTION__ << std::endl <<  "Invalied value cl_device_info." << std::endl;
	index=0;
LOOP_SUCCESS:
    return this->any[index];
}
