/*
 * Copyright 1993-2009 NVIDIA Corporation.  All rights reserved.
 *
 * NVIDIA Corporation and its licensors retain all intellectual property and 
 * proprietary rights in and to this software and related documentation. 
 * Any use, reproduction, disclosure, or distribution of this software 
 * and related documentation without an express license agreement from
 * NVIDIA Corporation is strictly prohibited.
 *
 * Please refer to the applicable NVIDIA end user license agreement (EULA) 
 * associated with this source code for terms and conditions that govern 
 * your use of this NVIDIA software.
 * 
 */

/* This sample queries the properties of the CUDA devices present in the system. */

/* Modified by Mark Zwolinski, December 2009 to make portable for all OpenCL systems */

// standard utilities and systems includes
//#include <oclUtils.h>
#ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sstream>
#include <fstream>

void clPrintDevInfo(cl_device_id device)
{
    char device_string[1024];


    // CL_DEVICE_NAME
    clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(device_string), &device_string, NULL);
   printf("  CL_DEVICE_NAME: \t\t\t%s\n", device_string);

    // CL_DEVICE_VENDOR
    clGetDeviceInfo(device, CL_DEVICE_VENDOR, sizeof(device_string), &device_string, NULL);
   printf("  CL_DEVICE_VENDOR: \t\t\t%s\n", device_string);
 
    // CL_DRIVER_VERSION
    clGetDeviceInfo(device, CL_DRIVER_VERSION, sizeof(device_string), &device_string, NULL);
   printf("  CL_DRIVER_VERSION: \t\t\t%s\n", device_string);

    // CL_DEVICE_INFO
    cl_device_type type;
    clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(type), &type, NULL);
    if( type & CL_DEVICE_TYPE_CPU )
       printf("  CL_DEVICE_TYPE:\t\t\t%s\n", "CL_DEVICE_TYPE_CPU");
    if( type & CL_DEVICE_TYPE_GPU )
       printf("  CL_DEVICE_TYPE:\t\t\t%s\n", "CL_DEVICE_TYPE_GPU");
    if( type & CL_DEVICE_TYPE_ACCELERATOR )
       printf("  CL_DEVICE_TYPE:\t\t\t%s\n", "CL_DEVICE_TYPE_ACCELERATOR");
    if( type & CL_DEVICE_TYPE_DEFAULT )
       printf("  CL_DEVICE_TYPE:\t\t\t%s\n", "CL_DEVICE_TYPE_DEFAULT");
    
    // CL_DEVICE_MAX_COMPUTE_UNITS
    cl_uint compute_units;
    clGetDeviceInfo(device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(compute_units), &compute_units, NULL);
   printf("  CL_DEVICE_MAX_COMPUTE_UNITS:\t\t%u\n", compute_units);

    // CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS
    size_t workitem_dims;
    clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof(workitem_dims), &workitem_dims, NULL);
   printf("  CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS:\t%u\n", workitem_dims);

    // CL_DEVICE_MAX_WORK_ITEM_SIZES
    size_t workitem_size[3];
    clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(workitem_size), &workitem_size, NULL);
   printf("  CL_DEVICE_MAX_WORK_ITEM_SIZES:\t%u / %u / %u \n", workitem_size[0], workitem_size[1], workitem_size[2]);
    
    // CL_DEVICE_MAX_WORK_GROUP_SIZE
    size_t workgroup_size;
    clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(workgroup_size), &workgroup_size, NULL);
   printf("  CL_DEVICE_MAX_WORK_GROUP_SIZE:\t%u\n", workgroup_size);

    // CL_DEVICE_MAX_CLOCK_FREQUENCY
    cl_uint clock_frequency;
    clGetDeviceInfo(device, CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(clock_frequency), &clock_frequency, NULL);
   printf("  CL_DEVICE_MAX_CLOCK_FREQUENCY:\t%u MHz\n", clock_frequency);

    // CL_DEVICE_ADDRESS_BITS
    cl_uint addr_bits;
    clGetDeviceInfo(device, CL_DEVICE_ADDRESS_BITS, sizeof(addr_bits), &addr_bits, NULL);
   printf("  CL_DEVICE_ADDRESS_BITS:\t\t%u\n", addr_bits);

    // CL_DEVICE_MAX_MEM_ALLOC_SIZE
    cl_ulong max_mem_alloc_size;
    clGetDeviceInfo(device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(max_mem_alloc_size), &max_mem_alloc_size, NULL);
   printf("  CL_DEVICE_MAX_MEM_ALLOC_SIZE:\t\t%u MByte\n", (unsigned int)(max_mem_alloc_size / (1024 * 1024)));

    // CL_DEVICE_GLOBAL_MEM_SIZE
    cl_ulong mem_size;
    clGetDeviceInfo(device, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(mem_size), &mem_size, NULL);
   printf("  CL_DEVICE_GLOBAL_MEM_SIZE:\t\t%u MByte\n", (unsigned int)(mem_size / (1024 * 1024)));

    // CL_DEVICE_ERROR_CORRECTION_SUPPORT
    cl_bool error_correction_support;
    clGetDeviceInfo(device, CL_DEVICE_ERROR_CORRECTION_SUPPORT, sizeof(error_correction_support), &error_correction_support, NULL);
   printf("  CL_DEVICE_ERROR_CORRECTION_SUPPORT:\t%s\n", error_correction_support == CL_TRUE ? "yes" : "no");

    // CL_DEVICE_LOCAL_MEM_TYPE
    cl_device_local_mem_type local_mem_type;
    clGetDeviceInfo(device, CL_DEVICE_LOCAL_MEM_TYPE, sizeof(local_mem_type), &local_mem_type, NULL);
   printf("  CL_DEVICE_LOCAL_MEM_TYPE:\t\t%s\n", local_mem_type == 1 ? "local" : "global");

    // CL_DEVICE_LOCAL_MEM_SIZE
    clGetDeviceInfo(device, CL_DEVICE_LOCAL_MEM_SIZE, sizeof(mem_size), &mem_size, NULL);
   printf("  CL_DEVICE_LOCAL_MEM_SIZE:\t\t%u KByte\n", (unsigned int)(mem_size / 1024));

    // CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE
    clGetDeviceInfo(device, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, sizeof(mem_size), &mem_size, NULL);
   printf("  CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE:\t%u KByte\n", (unsigned int)(mem_size / 1024));

    // CL_DEVICE_QUEUE_PROPERTIES
    cl_command_queue_properties queue_properties;
    clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, sizeof(queue_properties), &queue_properties, NULL);
    if( queue_properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE )
       printf("  CL_DEVICE_QUEUE_PROPERTIES:\t\t%s\n", "CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE");    
    if( queue_properties & CL_QUEUE_PROFILING_ENABLE )
       printf("  CL_DEVICE_QUEUE_PROPERTIES:\t\t%s\n", "CL_QUEUE_PROFILING_ENABLE");

    // CL_DEVICE_IMAGE_SUPPORT
    cl_bool image_support;
    clGetDeviceInfo(device, CL_DEVICE_IMAGE_SUPPORT, sizeof(image_support), &image_support, NULL);
   printf("  CL_DEVICE_IMAGE_SUPPORT:\t\t%u\n", image_support);

    // CL_DEVICE_MAX_READ_IMAGE_ARGS
    cl_uint max_read_image_args;
    clGetDeviceInfo(device, CL_DEVICE_MAX_READ_IMAGE_ARGS, sizeof(max_read_image_args), &max_read_image_args, NULL);
   printf("  CL_DEVICE_MAX_READ_IMAGE_ARGS:\t%u\n", max_read_image_args);

    // CL_DEVICE_MAX_WRITE_IMAGE_ARGS
    cl_uint max_write_image_args;
    clGetDeviceInfo(device, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, sizeof(max_write_image_args), &max_write_image_args, NULL);
   printf("  CL_DEVICE_MAX_WRITE_IMAGE_ARGS:\t%u\n", max_write_image_args);
    
    // 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
    size_t szMaxDims[5];
   printf("\n  CL_DEVICE_IMAGE <dim>"); 
    clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_WIDTH, sizeof(size_t), &szMaxDims[0], NULL);
   printf("\t\t\t2D_MAX_WIDTH\t %u\n", szMaxDims[0]);
    clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_HEIGHT, sizeof(size_t), &szMaxDims[1], NULL);
   printf("\t\t\t\t\t2D_MAX_HEIGHT\t %u\n", szMaxDims[1]);
    clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_WIDTH, sizeof(size_t), &szMaxDims[2], NULL);
   printf("\t\t\t\t\t3D_MAX_WIDTH\t %u\n", szMaxDims[2]);
    clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_HEIGHT, sizeof(size_t), &szMaxDims[3], NULL);
   printf("\t\t\t\t\t3D_MAX_HEIGHT\t %u\n", szMaxDims[3]);
    clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_DEPTH, sizeof(size_t), &szMaxDims[4], NULL);
   printf("\t\t\t\t\t3D_MAX_DEPTH\t %u\n", szMaxDims[4]);
    


 

    // CL_DEVICE_PREFERRED_VECTOR_WIDTH_<type>
   printf("  CL_DEVICE_PREFERRED_VECTOR_WIDTH_<t>\t"); 
    cl_uint vec_width [6];
    clGetDeviceInfo(device, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, sizeof(cl_uint), &vec_width[0], NULL);
    clGetDeviceInfo(device, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, sizeof(cl_uint), &vec_width[1], NULL);
    clGetDeviceInfo(device, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, sizeof(cl_uint), &vec_width[2], NULL);
    clGetDeviceInfo(device, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, sizeof(cl_uint), &vec_width[3], NULL);
    clGetDeviceInfo(device, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, sizeof(cl_uint), &vec_width[4], NULL);
    clGetDeviceInfo(device, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, sizeof(cl_uint), &vec_width[5], NULL);
   printf("CHAR %u, SHORT %u, INT %u, FLOAT %u, DOUBLE %u\n\n\n", 
           vec_width[0], vec_width[1], vec_width[2], vec_width[3], vec_width[4]); 
}





////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int main(int argc, const char** argv) 
{
    // start logs
    //shrSetLogFileName ("oclDeviceQuery.txt");
    //shrLog(LOGBOTH, 0, "oclDeviceQuery.exe Starting...\n\n"); 
    printf("clDeviceQuery Starting...\n\n"); 
    bool bPassed = true;
    std::string sProfileString = "clDeviceQuery, Platform Name = ";

    // Get OpenCL platform ID for NVIDIA if avaiable, otherwise default
    //shrLog(LOGBOTH, 0, "OpenCL SW Info:\n\n");
    printf("OpenCL SW Info:\n\n");
    char cBuffer[1024];
    cl_platform_id clSelectedPlatformID = NULL; 
    //cl_int ciErrNum = oclGetPlatformID (&clSelectedPlatformID);
    //shrCheckError(ciErrNum, CL_SUCCESS);

    cl_platform_id* clPlatformIDs;

    cl_uint num_platforms;
    cl_int ciErrNum = clGetPlatformIDs(0, NULL, &num_platforms);
    if (ciErrNum != CL_SUCCESS)
  {
        printf(" Error %i in clGetPlatformIDs Call!\n\n", ciErrNum);
        bPassed = false;
    }
    else 
    {
        if(num_platforms == 0)
      {
            printf("No OpenCL platform found!\n\n");
            bPassed = false;
        }
        else 
        {
            // if there's one platform or more, make space for ID's
            if ((clPlatformIDs = (cl_platform_id*)malloc(num_platforms * sizeof(cl_platform_id))) == NULL)
          {
    printf("Failed to allocate memory for cl_platform ID's!\n\n");
                bPassed = false;
          }

            // get platform info for each platform 
            ciErrNum = clGetPlatformIDs (num_platforms, clPlatformIDs, NULL);
            for(cl_uint i = 0; i < num_platforms; ++i)
          {
                ciErrNum = clGetPlatformInfo (clPlatformIDs[i], CL_PLATFORM_NAME, 1024, &cBuffer, NULL);
                if(ciErrNum == CL_SUCCESS)
                {
                        clSelectedPlatformID = clPlatformIDs[i];
                        // Get OpenCL platform name and version
                        ciErrNum = clGetPlatformInfo (clSelectedPlatformID, CL_PLATFORM_NAME, sizeof(cBuffer), cBuffer, NULL);
                        if (ciErrNum == CL_SUCCESS)
                       {
        //shrLog(LOGBOTH, 0, " CL_PLATFORM_NAME: \t%s\n", cBuffer);
                                printf(" CL_PLATFORM_NAME: \t%s\n", cBuffer);
        sProfileString += cBuffer;
      } 
      else
      {
        //shrLog(LOGBOTH, 0, " Error %i in clGetPlatformInfo Call !!!\n\n", ciErrNum);
        printf(" Error %i in clGetPlatformInfo Call !!!\n\n", ciErrNum);
        bPassed = false;
      }
      sProfileString += ", Platform Version = ";

      ciErrNum = clGetPlatformInfo (clSelectedPlatformID, CL_PLATFORM_VERSION, sizeof(cBuffer), cBuffer, NULL);
      if (ciErrNum == CL_SUCCESS)
      {
        //shrLog(LOGBOTH, 0, " CL_PLATFORM_VERSION: \t%s\n", cBuffer);
        printf(" CL_PLATFORM_VERSION: \t%s\n", cBuffer);
        sProfileString += cBuffer;
      } 
      else
      {
        //shrLog(LOGBOTH, 0, " Error %i in clGetPlatformInfo Call !!!\n\n", ciErrNum);
        printf(" Error %i in clGetPlatformInfo Call !!!\n\n", ciErrNum);
        bPassed = false;
      }
      //sProfileString += ", SDK Version = ";

      // Log OpenCL SDK Version # (for convenience:  not specific to OpenCL) 
      //shrLog(LOGBOTH, 0, " OpenCL SDK Version: \t%s\n\n\n", oclSDKVERSION);
      //sProfileString += oclSDKVERSION;
      sProfileString += ", NumDevs = ";

      // Get and log OpenCL device info 
      cl_uint ciDeviceCount;
      cl_device_id *devices;
      //shrLog(LOGBOTH, 0, "OpenCL Device Info:\n\n");
      printf("OpenCL Device Info:\n\n");
      ciErrNum = clGetDeviceIDs (clSelectedPlatformID, CL_DEVICE_TYPE_ALL, 0, NULL, &ciDeviceCount);

      // check for 0 devices found or errors... 
      if (ciDeviceCount == 0)
      {
        //shrLog(LOGBOTH, 0, " No devices found supporting OpenCL (return code %i)\n\n", ciErrNum);
        printf(" No devices found supporting OpenCL (return code %i)\n\n", ciErrNum);
        bPassed = false;
        sProfileString += "0";
      } 
      else if (ciErrNum != CL_SUCCESS)
      {
        //shrLog(LOGBOTH, 0, " Error %i in clGetDeviceIDs call !!!\n\n", ciErrNum);
        printf(" Error %i in clGetDeviceIDs call !!!\n\n", ciErrNum);
        bPassed = false;
      }
      else
      {
        // Get and log the OpenCL device ID's
        //shrLog(LOGBOTH, 0, " %u devices found supporting OpenCL:\n\n", ciDeviceCount);
        printf(" %u devices found supporting OpenCL:\n\n", ciDeviceCount);
        char cTemp[2];
        #ifdef WIN32
        sprintf_s(cTemp, 2*sizeof(char), "%u", ciDeviceCount);
        #else
        sprintf(cTemp, "%u", ciDeviceCount);
        #endif
        sProfileString += cTemp;
        if ((devices = (cl_device_id*)malloc(sizeof(cl_device_id) * ciDeviceCount)) == NULL)
        {
          //shrLog(LOGBOTH, 0, " Failed to allocate memory for devices !!!\n\n");
          printf(" Failed to allocate memory for devices !!!\n\n");
          bPassed = false;
        }
        ciErrNum = clGetDeviceIDs (clSelectedPlatformID, CL_DEVICE_TYPE_ALL, ciDeviceCount, devices, &ciDeviceCount);
        if (ciErrNum == CL_SUCCESS)
        {
          for(unsigned int i = 0; i < ciDeviceCount; ++i ) 
          {  
          //shrLog(LOGBOTH, 0, " ---------------------------------\n");
          printf(" ---------------------------------\n");
          clGetDeviceInfo(devices[i], CL_DEVICE_NAME, sizeof(cBuffer), &cBuffer, NULL);
          //shrLog(LOGBOTH, 0.0, " Device %s\n", cBuffer);
          //shrLog(LOGBOTH, 0, " ---------------------------------\n");
          printf(" Device %s\n", cBuffer);
          printf(" ---------------------------------\n");
          clPrintDevInfo(devices[i]);
          sProfileString += ", Device = ";
          sProfileString += cBuffer;
          }
        }
        else
        {
          //shrLog(LOGBOTH, 0, " Error %i in clGetDeviceIDs call !!!\n\n", ciErrNum);
          printf(" Error %i in clGetDeviceIDs call !!!\n\n", ciErrNum);
          bPassed = false;
        }
      }

      // masterlog info
      sProfileString += "\n";
      //shrLog(LOGBOTH | MASTER, 0, sProfileString.c_str());
      printf("%s", sProfileString.c_str());
    }
    free(clPlatformIDs);
    }
  }
    }
    // Log system info(for convenience:  not specific to OpenCL) 
    //shrLog(LOGBOTH, 0, "\nSystem Info: \n\n");
    printf( "\nSystem Info: \n\n");
    #ifdef _WIN32
        SYSTEM_INFO stProcInfo;         // processor info struct
        OSVERSIONINFO stOSVerInfo;      // Win OS info struct
        SYSTEMTIME stLocalDateTime;     // local date / time struct 

        // processor
        SecureZeroMemory(&stProcInfo, sizeof(SYSTEM_INFO));
        GetSystemInfo(&stProcInfo);

        // OS
        SecureZeroMemory(&stOSVerInfo, sizeof(OSVERSIONINFO));
        stOSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        GetVersionEx(&stOSVerInfo);

        // date and time
        GetLocalTime(&stLocalDateTime); 

        // write time and date to logs
        //shrLog(LOGBOTH, 0, " Local Time/Date = %i:%i:%i, %i/%i/%i\n", 
        //    stLocalDateTime.wHour, stLocalDateTime.wMinute, stLocalDateTime.wSecond, 
        //    stLocalDateTime.wMonth, stLocalDateTime.wDay, stLocalDateTime.wYear); 
        printf(" Local Time/Date = %i:%i:%i, %i/%i/%i\n", 
            stLocalDateTime.wHour, stLocalDateTime.wMinute, stLocalDateTime.wSecond, 
            stLocalDateTime.wMonth, stLocalDateTime.wDay, stLocalDateTime.wYear); 

        // write proc and OS info to logs
        //shrLog(LOGBOTH, 0, " CPU Arch: %i\n CPU Level: %i\n # of CPU processors: %u\n Windows Build: %u\n Windows Ver: %u.%u\n\n\n", 
        //    stProcInfo.wProcessorArchitecture, stProcInfo.wProcessorLevel, stProcInfo.dwNumberOfProcessors, 
        //    stOSVerInfo.dwBuildNumber, stOSVerInfo.dwMajorVersion, stOSVerInfo.dwMinorVersion);
        printf(" CPU Arch: %i\n CPU Level: %i\n # of CPU processors: %u\n Windows Build: %u\n Windows Ver: %u.%u\n\n\n", 
            stProcInfo.wProcessorArchitecture, stProcInfo.wProcessorLevel, stProcInfo.dwNumberOfProcessors, 
            stOSVerInfo.dwBuildNumber, stOSVerInfo.dwMajorVersion, stOSVerInfo.dwMinorVersion);
    #endif

    #ifdef MAC
  #else
    #ifdef UNIX
        char timestr[255];
        time_t now = time(NULL);
        struct tm  *ts;

        ts = localtime(&now);
        
        strftime(timestr, 255, " %H:%M:%S, %m/%d/%Y",ts);
        
        // write time and date to logs
        //shrLog(LOGBOTH, 0, " Local Time/Date = %s\n", 
        //    timestr); 
        printf(" Local Time/Date = %s\n", timestr); 

        // write proc and OS info to logs
        
        // parse /proc/cpuinfo
        std::ifstream cpuinfo( "/proc/cpuinfo" ); // open the file in /proc        
        std::string tmp;

        int cpu_num = 0;
        std::string cpu_name = "none";        

        do
    {
            cpuinfo >> tmp;
            
            if( tmp == "processor" )
                cpu_num++;
            
            if( tmp == "name" )
      {
                cpuinfo >> tmp; // skip :

                std::stringstream tmp_stream("");
                do
        {
                    cpuinfo >> tmp;
                    if (tmp != std::string("stepping"))
          {
                        tmp_stream << tmp.c_str() << " ";
                    }
                    
                }
        while (tmp != std::string("stepping"));
                
                cpu_name = tmp_stream.str();
            }

        }
    while ( (! cpuinfo.eof()) );

        // Linux version
        std::ifstream version( "/proc/version" );
        char versionstr[255];

        version.getline(versionstr, 255);

        //shrLog(LOGBOTH, 0, " CPU Name: %s\n # of CPU processors: %u\n %s\n\n\n", 
        //       cpu_name.c_str(),cpu_num,versionstr);
        printf(" CPU Name: %s\n # of CPU processors: %u\n %s\n\n\n", 
               cpu_name.c_str(),cpu_num,versionstr);
    #endif
    #endif

    // finish
    //shrLog(LOGBOTH, 0, "TEST %s\n\n", bPassed ? "PASSED" : "FAILED !!!"); 
    printf("TEST %s\n\n", bPassed ? "PASSED" : "FAILED !!!"); 
    //shrEXIT(argc, argv);
}
