/*=========================================================================
 *
 *  Copyright UMC Utrecht and contributors
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#include "itkOpenCLStringUtils.h"

#include <iosfwd>
#include <sstream>
#include <cstring>

namespace itk
{
inline bool
opencl_isspace(const char c)
{
  switch (c)
  {
    case '\t':
    case '\n':
    case '\r':
    case '\f':
    case ' ':
      return true;

    default:
      return false;
  }
}


//------------------------------------------------------------------------------
std::string
opencl_simplified(const std::string & str)
{
  if (str.empty())
  {
    return str;
  }

  const std::size_t size = str.size();
  std::string       result(size, '\0');
  const char *      from = &str[0];
  const char *      fromend = from + size;
  int               outc = 0;
  char *            to = &result[0];
  for (;;)
  {
    while (from != fromend && opencl_isspace(char(*from)))
    {
      ++from;
    }
    while (from != fromend && !opencl_isspace(char(*from)))
    {
      to[outc++] = *from++;
    }
    if (from != fromend)
    {
      to[outc++] = ' ';
    }
    else
    {
      break;
    }
  }
  if (outc > 0 && to[outc - 1] == ' ')
  {
    --outc;
  }
  result.resize(outc);

  return result;
}


//------------------------------------------------------------------------------
bool
opencl_has_extension(const std::string & list, const std::string & name)
{
  if (list.empty() || name.empty())
  {
    return false;
  }
  const std::size_t found = list.find(name);
  if (found != std::string::npos)
  {
    return true;
  }
  else
  {
    return false;
  }
}


//------------------------------------------------------------------------------
std::string
opencl_get_platform_info_string(const cl_platform_id id, const cl_platform_info name)
{
  std::size_t size;

  if (!id || clGetPlatformInfo(id, name, 0, 0, &size) != CL_SUCCESS)
  {
    return std::string();
  }
  std::string buffer(size, '\0');
  clGetPlatformInfo(id, name, size, &buffer[0], &size);
  return buffer;
}


//------------------------------------------------------------------------------
std::string
opencl_get_device_info_string(const cl_device_id id, const cl_device_info name)
{
  std::size_t size;

  if (!id || clGetDeviceInfo(id, name, 0, 0, &size) != CL_SUCCESS)
  {
    return std::string();
  }
  std::string buffer(size, '\0');
  clGetDeviceInfo(id, name, size, &buffer[0], &size);
  return buffer;
}


//------------------------------------------------------------------------------
bool
opencl_is_platform(cl_platform_id id, cl_platform_info name, const char * str)
{
  std::size_t len = strlen(str);
  std::size_t size;

  if (!id || clGetPlatformInfo(id, name, 0, 0, &size) != CL_SUCCESS)
  {
    return false;
  }
  if (size <= len)
  {
    return false;
  }
  std::string buffer(size, '\0');
  clGetPlatformInfo(id, name, size, &buffer[0], &size);
  if (strncmp(&buffer[0], str, len) != 0)
  {
    return false;
  }
  return buffer[len] == '\0';
}


//------------------------------------------------------------------------------
int
opencl_version_flags(const std::string & /*version*/)
{
  // not implemented.
  return 1;
}


//------------------------------------------------------------------------------
std::list<std::string>
opencl_split_string(const std::string & str, const char separator)
{
  std::list<std::string> strings;

  if (str.empty())
  {
    return strings;
  }

  std::istringstream f(str);
  std::string        s;

  while (std::getline(f, s, separator))
  {
    strings.push_back(s);
  }
  return strings;
}


//------------------------------------------------------------------------------
unsigned int
opencl_get_device_info_uint(const cl_device_id id, const cl_device_info name)
{
  cl_uint value;

  if (!id || clGetDeviceInfo(id, name, sizeof(value), &value, 0) != CL_SUCCESS)
  {
    return 0;
  }
  else
  {
    const unsigned int result = value;
    return result;
  }
}


//------------------------------------------------------------------------------
int
opencl_get_device_info_int(const cl_device_id id, const cl_device_info name)
{
  cl_int value;

  if (!id || clGetDeviceInfo(id, name, sizeof(value), &value, 0) != CL_SUCCESS)
  {
    return 0;
  }
  else
  {
    const int result = value;
    return result;
  }
}


//------------------------------------------------------------------------------
unsigned long
opencl_get_device_info_ulong(const cl_device_id id, const cl_device_info name)
{
  cl_ulong value;

  if (!id || clGetDeviceInfo(id, name, sizeof(value), &value, 0) != CL_SUCCESS)
  {
    return 0;
  }
  else
  {
    const unsigned long result = value;
    return result;
  }
}


//------------------------------------------------------------------------------
std::size_t
opencl_get_device_info_size(const cl_device_id id, const cl_device_info name)
{
  std::size_t value;

  if (!id || clGetDeviceInfo(id, name, sizeof(value), &value, 0) != CL_SUCCESS)
  {
    return 0;
  }
  else
  {
    return value;
  }
}


//------------------------------------------------------------------------------
bool
opencl_get_device_info_bool(const cl_device_id id, const cl_device_info name)
{
  cl_bool value;

  if (!id || clGetDeviceInfo(id, name, sizeof(value), &value, 0) != CL_SUCCESS)
  {
    return false;
  }
  else
  {
    return value != 0;
  }
}


//------------------------------------------------------------------------------
bool
opencl_get_device_info_is_string(const cl_device_id id, const cl_device_info name, const char * str)
{
  std::size_t len = strlen(str);
  std::size_t size;

  if (!id || clGetDeviceInfo(id, name, 0, 0, &size) != CL_SUCCESS)
  {
    return false;
  }
  if (size <= len)
  {
    return false;
  }
  std::string buffer(size, '\0');
  clGetDeviceInfo(id, name, size, &buffer[0], &size);
  if (strncmp(&buffer[0], str, len) != 0)
  {
    return false;
  }
  return buffer[len] == '\0';
}


} // end of namespace itk
