 import Foreign.C.Types
 import Foreign.C.String( CString, withCString, newCString, peekCString )
 import System.GPU.OpenCL.Types( 
-  CLint, CLuint, CLProgram, CLContext, CLKernel, CLDeviceID, CLError(..), 
-  CLProgramInfo_, CLBuildStatus(..), CLBuildStatus_, CLProgramBuildInfo_, 
-  CLKernelInfo_, CLKernelWorkGroupInfo_, wrapCheckSuccess, wrapPError, 
-  wrapGetInfo, getCLValue, getEnumCL )
+  CLint, CLuint, CLulong, CLProgram, CLContext, CLKernel, CLDeviceID, 
+  CLError(..), CLProgramInfo_, CLBuildStatus(..), CLBuildStatus_, 
+  CLProgramBuildInfo_, CLKernelInfo_, CLKernelWorkGroupInfo_, wrapCheckSuccess, 
+  wrapPError, wrapGetInfo, getCLValue, getEnumCL )
 
 #include <CL/cl.h>
 

clReleaseKernel :: CLKernel -> IO Bool
clReleaseKernel krn = wrapCheckSuccess $ raw_clReleaseKernel krn

#c
enum CLKernelInfo {
  cL_KERNEL_FUNCTION_NAME=CL_KERNEL_FUNCTION_NAME,

      infoid = getCLValue CL_KERNEL_PROGRAM
      size = fromIntegral $ sizeOf (nullPtr::CLProgram)


#c
enum CLKernelGroupInfo {
  cL_KERNEL_WORK_GROUP_SIZE=CL_KERNEL_WORK_GROUP_SIZE,
  cL_KERNEL_COMPILE_WORK_GROUP_SIZE=CL_KERNEL_COMPILE_WORK_GROUP_SIZE,
  cL_KERNEL_LOCAL_MEM_SIZE=CL_KERNEL_LOCAL_MEM_SIZE,
  };
#endc
{#enum CLKernelGroupInfo {upcaseFirstLetter} #}

-- | This provides a mechanism for the application to query the work-group size
-- that can be used to execute a kernel on a specific device given by
-- device. The OpenCL implementation uses the resource requirements of the
-- kernel (register usage etc.) to determine what this work-group size should
-- be.
clGetKernelWorkGroupSize :: CLKernel -> CLDeviceID -> IO (Either CLError CSize)
clGetKernelWorkGroupSize krn device = wrapGetInfo (\(dat :: Ptr CSize)
                                                   -> raw_clGetKernelWorkGroupInfo krn device infoid size (castPtr dat)) id
    where
      infoid = getCLValue CL_KERNEL_WORK_GROUP_SIZE
      size = fromIntegral $ sizeOf (0::CSize)

-- | Returns the work-group size specified by the __attribute__((reqd_work_gr
-- oup_size(X, Y, Z))) qualifier. See Function Qualifiers. If the work-group
-- size is not specified using the above attribute qualifier (0, 0, 0) is
-- returned.
clGetKernelCompileWorkGroupSize :: CLKernel -> CLDeviceID -> IO (Either CLError [CSize])
clGetKernelCompileWorkGroupSize krn device = do
  allocaArray num $ \(buff :: Ptr CSize) -> do
    errcode <- raw_clGetKernelWorkGroupInfo krn device infoid size (castPtr buff) nullPtr
    if errcode == (fromIntegral . fromEnum $ CL_SUCCESS)
      then fmap Right $ peekArray num buff
      else return . Left . toEnum . fromIntegral $ errcode
    where 
      infoid = getCLValue CL_KERNEL_COMPILE_WORK_GROUP_SIZE
      num = 3
      elemSize = fromIntegral $ sizeOf (0::CSize)
      size = fromIntegral $ num * elemSize


-- | Returns the amount of local memory in bytes being used by a kernel. This
-- includes local memory that may be needed by an implementation to execute the
-- kernel, variables declared inside the kernel with the __local address
-- qualifier and local memory to be allocated for arguments to the kernel
-- declared as pointers with the __local address qualifier and whose size is
-- specified with 'clSetKernelArg'.
--
-- If the local memory size, for any pointer argument to the kernel declared
-- with the __local address qualifier, is not specified, its size is assumed to
-- be 0.
clGetKernelLocalMemSize :: CLKernel -> CLDeviceID -> IO (Either CLError CLulong)
clGetKernelLocalMemSize krn device = wrapGetInfo (\(dat :: Ptr CLulong)
                                                  -> raw_clGetKernelWorkGroupInfo krn device infoid size (castPtr dat)) id
    where
      infoid = getCLValue CL_KERNEL_LOCAL_MEM_SIZE
      size = fromIntegral $ sizeOf (0::CLulong)
-- -----------------------------------------------------------------------------
