/*
 * Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
 *
 * SPDX-License-Identifier: MIT
 */

#pragma once

#include <hip/driver_types.h>
#include <hip/texture_types.h>

namespace hip {
inline cl_channel_type getCLChannelType(const hipArray_Format hipFormat,
                                        const hipTextureReadMode hipReadMode) {
  if (hipReadMode == hipReadModeElementType) {
    switch (hipFormat) {
      case HIP_AD_FORMAT_UNSIGNED_INT8:
        return CL_UNSIGNED_INT8;
      case HIP_AD_FORMAT_SIGNED_INT8:
        return CL_SIGNED_INT8;
      case HIP_AD_FORMAT_UNSIGNED_INT16:
        return CL_UNSIGNED_INT16;
      case HIP_AD_FORMAT_SIGNED_INT16:
        return CL_SIGNED_INT16;
      case HIP_AD_FORMAT_UNSIGNED_INT32:
        return CL_UNSIGNED_INT32;
      case HIP_AD_FORMAT_SIGNED_INT32:
        return CL_SIGNED_INT32;
      case HIP_AD_FORMAT_HALF:
        return CL_HALF_FLOAT;
      case HIP_AD_FORMAT_FLOAT:
        return CL_FLOAT;
    }
  } else if (hipReadMode == hipReadModeNormalizedFloat) {
    switch (hipFormat) {
      case HIP_AD_FORMAT_UNSIGNED_INT8:
        return CL_UNORM_INT8;
      case HIP_AD_FORMAT_SIGNED_INT8:
        return CL_SNORM_INT8;
      case HIP_AD_FORMAT_UNSIGNED_INT16:
        return CL_UNORM_INT16;
      case HIP_AD_FORMAT_SIGNED_INT16:
        return CL_SNORM_INT16;
      case HIP_AD_FORMAT_UNSIGNED_INT32:
        return CL_UNSIGNED_INT32;
      case HIP_AD_FORMAT_SIGNED_INT32:
        return CL_SIGNED_INT32;
      case HIP_AD_FORMAT_HALF:
        return CL_HALF_FLOAT;
      case HIP_AD_FORMAT_FLOAT:
        return CL_FLOAT;
    }
  }

  // error scenario
  return {};
}

inline cl_channel_order getCLChannelOrder(const unsigned int hipNumChannels, const int sRGB) {
  switch (hipNumChannels) {
    case 1:
      return CL_R;
    case 2:
      return CL_RG;
    case 4:
      return (sRGB == 1) ? CL_sRGBA : CL_RGBA;
    default:
      break;
  }

  // error scenario
  return {};
}

inline cl_mem_object_type getCLMemObjectType(const unsigned int hipWidth,
                                             const unsigned int hipHeight,
                                             const unsigned int hipDepth,
                                             const unsigned int flags) {
  if ((flags & hipArrayLayered) == hipArrayLayered) {
    if ((hipWidth != 0) && (hipHeight == 0) && (hipDepth != 0)) {
      return CL_MEM_OBJECT_IMAGE1D_ARRAY;
    } else if ((hipWidth != 0) && (hipHeight != 0) && (hipDepth != 0)) {
      return CL_MEM_OBJECT_IMAGE2D_ARRAY;
    }
  } else {
    if ((hipWidth != 0) && (hipHeight == 0) && (hipDepth == 0)) {
      return CL_MEM_OBJECT_IMAGE1D;
    } else if ((hipWidth != 0) && (hipHeight != 0) && (hipDepth == 0)) {
      return CL_MEM_OBJECT_IMAGE2D;
    } else if ((hipWidth != 0) && (hipHeight != 0) && (hipDepth != 0)) {
      return CL_MEM_OBJECT_IMAGE3D;
    }
  }
  // error scenario. ShouldNotReachHere()
  return CL_MEM_OBJECT_ALLOCATION_FAILURE;
}

inline cl_mem_object_type getCLMemObjectType(const hipArray* arr) {
  const cl_mem dstMemObj = reinterpret_cast<const cl_mem>(arr->data);
  const amd::Image* dstImage = as_amd(dstMemObj)->asImage();
  return dstImage ? dstImage->getType() : CL_MEM_OBJECT_ALLOCATION_FAILURE;
}

inline bool isLayered1D(const hipArray* arr) {
  return CL_MEM_OBJECT_IMAGE1D_ARRAY == getCLMemObjectType(arr);
}

inline cl_addressing_mode getCLAddressingMode(const hipTextureAddressMode hipAddressMode) {
  switch (hipAddressMode) {
    case hipAddressModeWrap:
      return CL_ADDRESS_REPEAT;
    case hipAddressModeClamp:
      return CL_ADDRESS_CLAMP_TO_EDGE;
    case hipAddressModeMirror:
      return CL_ADDRESS_MIRRORED_REPEAT;
    case hipAddressModeBorder:
      return CL_ADDRESS_CLAMP;
  }

  // error scenario
  return {};
}

inline cl_filter_mode getCLFilterMode(const hipTextureFilterMode hipFilterMode) {
  switch (hipFilterMode) {
    case hipFilterModePoint:
      return CL_FILTER_NEAREST;
    case hipFilterModeLinear:
      return CL_FILTER_LINEAR;
  }

  // error scenario
  return {};
}

inline cl_mem_object_type getCLMemObjectType(const hipResourceType hipResType) {
  switch (hipResType) {
    case hipResourceTypeLinear:
      return CL_MEM_OBJECT_IMAGE1D_BUFFER;
    case hipResourceTypePitch2D:
      return CL_MEM_OBJECT_IMAGE2D;
    default:
      break;
  }

  // error scenario
  return {};
}

inline hipArray_Format getCL2hipArrayFormat(const cl_channel_type type) {
  switch (type) {
    case CL_SNORM_INT8:
    case CL_SIGNED_INT8:
      return HIP_AD_FORMAT_SIGNED_INT8;

    case CL_UNSIGNED_INT16:
      return HIP_AD_FORMAT_UNSIGNED_INT16;

    case CL_SIGNED_INT16:
      return HIP_AD_FORMAT_SIGNED_INT16;

    case CL_SIGNED_INT32:
      return HIP_AD_FORMAT_SIGNED_INT32;

    case CL_UNSIGNED_INT32:
      return HIP_AD_FORMAT_UNSIGNED_INT32;

    case CL_FLOAT:
      return HIP_AD_FORMAT_FLOAT;

    case CL_UNSIGNED_INT8:
    case CL_UNORM_INT8:
    case CL_UNORM_INT_101010:
    default:
      return HIP_AD_FORMAT_UNSIGNED_INT8;
  }
}
inline size_t getElementSize(const hipArray_const_t array) {
  switch (array->Format) {
    case HIP_AD_FORMAT_UNSIGNED_INT8:
    case HIP_AD_FORMAT_SIGNED_INT8:
      return 1 * array->NumChannels;
    case HIP_AD_FORMAT_UNSIGNED_INT16:
    case HIP_AD_FORMAT_SIGNED_INT16:
    case HIP_AD_FORMAT_HALF:
      return 2 * array->NumChannels;
    case HIP_AD_FORMAT_UNSIGNED_INT32:
    case HIP_AD_FORMAT_SIGNED_INT32:
    case HIP_AD_FORMAT_FLOAT:
      return 4 * array->NumChannels;
  }

  // error scenario
  return {};
}

inline hipChannelFormatDesc getChannelFormatDesc(int numChannels, hipArray_Format arrayFormat) {
  switch (arrayFormat) {
    case HIP_AD_FORMAT_UNSIGNED_INT8:
      switch (numChannels) {
        case 1:
          return {8, 0, 0, 0, hipChannelFormatKindUnsigned};
        case 2:
          return {8, 8, 0, 0, hipChannelFormatKindUnsigned};
        case 4:
          return {8, 8, 8, 8, hipChannelFormatKindUnsigned};
      }
    case HIP_AD_FORMAT_SIGNED_INT8:
      switch (numChannels) {
        case 1:
          return {8, 0, 0, 0, hipChannelFormatKindSigned};
        case 2:
          return {8, 8, 0, 0, hipChannelFormatKindSigned};
        case 4:
          return {8, 8, 8, 8, hipChannelFormatKindSigned};
      }
    case HIP_AD_FORMAT_UNSIGNED_INT16:
      switch (numChannels) {
        case 1:
          return {16, 0, 0, 0, hipChannelFormatKindUnsigned};
        case 2:
          return {16, 16, 0, 0, hipChannelFormatKindUnsigned};
        case 4:
          return {16, 16, 16, 16, hipChannelFormatKindUnsigned};
      }
    case HIP_AD_FORMAT_SIGNED_INT16:
      switch (numChannels) {
        case 1:
          return {16, 0, 0, 0, hipChannelFormatKindSigned};
        case 2:
          return {16, 16, 0, 0, hipChannelFormatKindSigned};
        case 4:
          return {16, 16, 16, 16, hipChannelFormatKindSigned};
      }
    case HIP_AD_FORMAT_UNSIGNED_INT32:
      switch (numChannels) {
        case 1:
          return {32, 0, 0, 0, hipChannelFormatKindUnsigned};
        case 2:
          return {32, 32, 0, 0, hipChannelFormatKindUnsigned};
        case 4:
          return {32, 32, 32, 32, hipChannelFormatKindUnsigned};
      }
    case HIP_AD_FORMAT_SIGNED_INT32:
      switch (numChannels) {
        case 1:
          return {32, 0, 0, 0, hipChannelFormatKindSigned};
        case 2:
          return {32, 32, 0, 0, hipChannelFormatKindSigned};
        case 4:
          return {32, 32, 32, 32, hipChannelFormatKindSigned};
      }
    case HIP_AD_FORMAT_HALF:
      switch (numChannels) {
        case 1:
          return {16, 0, 0, 0, hipChannelFormatKindFloat};
        case 2:
          return {16, 16, 0, 0, hipChannelFormatKindFloat};
        case 4:
          return {16, 16, 16, 16, hipChannelFormatKindFloat};
      }
    case HIP_AD_FORMAT_FLOAT:
      switch (numChannels) {
        case 1:
          return {32, 0, 0, 0, hipChannelFormatKindFloat};
        case 2:
          return {32, 32, 0, 0, hipChannelFormatKindFloat};
        case 4:
          return {32, 32, 32, 32, hipChannelFormatKindFloat};
      }
  }

  // error scenario
  return {};
}

inline unsigned int getNumChannels(const hipChannelFormatDesc& desc) {
  return ((desc.x != 0) + (desc.y != 0) + (desc.z != 0) + (desc.w != 0));
}

inline bool CheckArrayFormat(const hipChannelFormatDesc& desc) {
  if (desc.x == 0) {
    return false;
  } else {
    if (desc.y != 0 && desc.y != desc.x) {
      return false;
    }
    if (desc.z != 0 && desc.z != desc.x) {
      return false;
    }
    if (desc.w != 0 && desc.w != desc.x) {
      return false;
    }
  }
  // The bit channel description should not allow any channels after a zero channel
  if (desc.y == 0) {
    return !(desc.z > 0 || desc.w > 0);
  } else if (desc.z == 0) {
    return !(desc.w > 0);
  }

  return true;
}

inline hipArray_Format getArrayFormat(const hipChannelFormatDesc& desc) {
  switch (desc.f) {
    case hipChannelFormatKindUnsigned:
      switch (desc.x) {
        case 8:
          return HIP_AD_FORMAT_UNSIGNED_INT8;
        case 16:
          return HIP_AD_FORMAT_UNSIGNED_INT16;
        case 32:
          return HIP_AD_FORMAT_UNSIGNED_INT32;
      }
    case hipChannelFormatKindSigned:
      switch (desc.x) {
        case 8:
          return HIP_AD_FORMAT_SIGNED_INT8;
        case 16:
          return HIP_AD_FORMAT_SIGNED_INT16;
        case 32:
          return HIP_AD_FORMAT_SIGNED_INT32;
      }
    case hipChannelFormatKindFloat:
      switch (desc.x) {
        case 16:
          return HIP_AD_FORMAT_HALF;
        case 32:
          return HIP_AD_FORMAT_FLOAT;
      }
    default:
      break;
  }

  // error scenario
  return {};
}

inline int getNumChannels(const hipResourceViewFormat hipFormat) {
  switch (hipFormat) {
    case hipResViewFormatUnsignedChar1:
    case hipResViewFormatSignedChar1:
    case hipResViewFormatUnsignedShort1:
    case hipResViewFormatSignedShort1:
    case hipResViewFormatUnsignedInt1:
    case hipResViewFormatSignedInt1:
    case hipResViewFormatHalf1:
    case hipResViewFormatFloat1:
      return 1;
    case hipResViewFormatUnsignedChar2:
    case hipResViewFormatSignedChar2:
    case hipResViewFormatUnsignedShort2:
    case hipResViewFormatSignedShort2:
    case hipResViewFormatUnsignedInt2:
    case hipResViewFormatSignedInt2:
    case hipResViewFormatHalf2:
    case hipResViewFormatFloat2:
      return 2;
    case hipResViewFormatUnsignedChar4:
    case hipResViewFormatSignedChar4:
    case hipResViewFormatUnsignedShort4:
    case hipResViewFormatSignedShort4:
    case hipResViewFormatUnsignedInt4:
    case hipResViewFormatSignedInt4:
    case hipResViewFormatHalf4:
    case hipResViewFormatFloat4:
      return 4;
    default:
      break;
  }

  // error scenario
  return {};
}

inline hipArray_Format getArrayFormat(const hipResourceViewFormat hipFormat) {
  switch (hipFormat) {
    case hipResViewFormatUnsignedChar1:
    case hipResViewFormatUnsignedChar2:
    case hipResViewFormatUnsignedChar4:
      return HIP_AD_FORMAT_UNSIGNED_INT8;
    case hipResViewFormatSignedChar1:
    case hipResViewFormatSignedChar2:
    case hipResViewFormatSignedChar4:
      return HIP_AD_FORMAT_SIGNED_INT8;
    case hipResViewFormatUnsignedShort1:
    case hipResViewFormatUnsignedShort2:
    case hipResViewFormatUnsignedShort4:
      return HIP_AD_FORMAT_UNSIGNED_INT16;
    case hipResViewFormatSignedShort1:
    case hipResViewFormatSignedShort2:
    case hipResViewFormatSignedShort4:
      return HIP_AD_FORMAT_SIGNED_INT16;
    case hipResViewFormatUnsignedInt1:
    case hipResViewFormatUnsignedInt2:
    case hipResViewFormatUnsignedInt4:
      return HIP_AD_FORMAT_UNSIGNED_INT32;
    case hipResViewFormatSignedInt1:
    case hipResViewFormatSignedInt2:
    case hipResViewFormatSignedInt4:
      return HIP_AD_FORMAT_SIGNED_INT32;
    case hipResViewFormatHalf1:
    case hipResViewFormatHalf2:
    case hipResViewFormatHalf4:
      return HIP_AD_FORMAT_HALF;
    case hipResViewFormatFloat1:
    case hipResViewFormatFloat2:
    case hipResViewFormatFloat4:
      return HIP_AD_FORMAT_FLOAT;
    default:
      break;
  }

  // error scenario
  return {};
}

inline hipResourceViewFormat getResourceViewFormat(const hipChannelFormatDesc& desc) {
  switch (desc.f) {
    case hipChannelFormatKindUnsigned:
      switch (getNumChannels(desc)) {
        case 1:
          switch (desc.x) {
            case 8:
              return hipResViewFormatUnsignedChar1;
            case 16:
              return hipResViewFormatUnsignedShort1;
            case 32:
              return hipResViewFormatUnsignedInt1;
          }
        case 2:
          switch (desc.x) {
            case 8:
              return hipResViewFormatUnsignedChar2;
            case 16:
              return hipResViewFormatUnsignedShort2;
            case 32:
              return hipResViewFormatUnsignedInt2;
          }
        case 4:
          switch (desc.x) {
            case 8:
              return hipResViewFormatUnsignedChar4;
            case 16:
              return hipResViewFormatUnsignedShort4;
            case 32:
              return hipResViewFormatUnsignedInt4;
          }
      }
    case hipChannelFormatKindSigned:
      switch (getNumChannels(desc)) {
        case 1:
          switch (desc.x) {
            case 8:
              return hipResViewFormatSignedChar1;
            case 16:
              return hipResViewFormatSignedShort1;
            case 32:
              return hipResViewFormatSignedInt1;
          }
        case 2:
          switch (desc.x) {
            case 8:
              return hipResViewFormatSignedChar2;
            case 16:
              return hipResViewFormatSignedShort2;
            case 32:
              return hipResViewFormatSignedInt2;
          }
        case 4:
          switch (desc.x) {
            case 8:
              return hipResViewFormatSignedChar4;
            case 16:
              return hipResViewFormatSignedShort4;
            case 32:
              return hipResViewFormatSignedInt4;
          }
      }
    case hipChannelFormatKindFloat:
      switch (getNumChannels(desc)) {
        case 1:
          switch (desc.x) {
            case 16:
              return hipResViewFormatHalf1;
            case 32:
              return hipResViewFormatFloat1;
          }
        case 2:
          switch (desc.x) {
            case 16:
              return hipResViewFormatHalf2;
            case 32:
              return hipResViewFormatFloat2;
          }
        case 4:
          switch (desc.x) {
            case 16:
              return hipResViewFormatHalf4;
            case 32:
              return hipResViewFormatFloat4;
          }
      }
    default:
      break;
  }

  // error scenario
  return {};
}

inline hipTextureDesc getTextureDesc(const textureReference* texRef) {
  hipTextureDesc texDesc = {};
  std::memcpy(texDesc.addressMode, texRef->addressMode, sizeof(texDesc.addressMode));
  texDesc.filterMode = texRef->filterMode;
  texDesc.readMode = texRef->readMode;
  texDesc.sRGB = texRef->sRGB;
  texDesc.normalizedCoords = texRef->normalized;
  texDesc.maxAnisotropy = texRef->maxAnisotropy;
  texDesc.mipmapFilterMode = texRef->mipmapFilterMode;
  texDesc.mipmapLevelBias = texRef->mipmapLevelBias;
  texDesc.minMipmapLevelClamp = texRef->minMipmapLevelClamp;
  texDesc.maxMipmapLevelClamp = texRef->maxMipmapLevelClamp;

  return texDesc;
}

inline hipResourceViewDesc getResourceViewDesc(hipArray_const_t array,
                                               const hipResourceViewFormat format) {
  hipResourceViewDesc resViewDesc = {};
  resViewDesc.format = format;
  resViewDesc.width = array->width;
  resViewDesc.height = array->height;
  resViewDesc.depth = array->depth;
  resViewDesc.firstMipmapLevel = 0;
  resViewDesc.lastMipmapLevel = 0;
  resViewDesc.firstLayer = 0;
  resViewDesc.lastLayer = 0; /* TODO add hipArray::numLayers */

  return resViewDesc;
}

inline hipResourceViewDesc getResourceViewDesc(hipMipmappedArray_const_t array,
                                               const hipResourceViewFormat format) {
  hipResourceViewDesc resViewDesc = {};
  resViewDesc.format = format;
  resViewDesc.width = array->width;
  resViewDesc.height = array->height;
  resViewDesc.depth = array->depth;
  resViewDesc.firstMipmapLevel = 0;
  resViewDesc.lastMipmapLevel = 0; /* TODO add hipMipmappedArray::numMipLevels */
  resViewDesc.firstLayer = 0;
  resViewDesc.lastLayer = 0; /* TODO add hipArray::numLayers */

  return resViewDesc;
}

inline std::pair<hipMemoryType, hipMemoryType> getMemoryType(const hipMemcpyKind kind) {
  switch (kind) {
    case hipMemcpyHostToHost:
      return {hipMemoryTypeHost, hipMemoryTypeHost};
    case hipMemcpyHostToDevice:
      return {hipMemoryTypeHost, hipMemoryTypeDevice};
    case hipMemcpyDeviceToHost:
      return {hipMemoryTypeDevice, hipMemoryTypeHost};
    case hipMemcpyDeviceToDevice:
    case hipMemcpyDeviceToDeviceNoCU:
      return {hipMemoryTypeDevice, hipMemoryTypeDevice};
    case hipMemcpyDefault:
      return {hipMemoryTypeUnified, hipMemoryTypeUnified};
  }

  // error scenario
  return {};
}

inline HIP_MEMCPY3D getDrvMemcpy3DDesc(const hip_Memcpy2D& desc2D) {
  HIP_MEMCPY3D desc3D = {};

  desc3D.srcXInBytes = desc2D.srcXInBytes;
  desc3D.srcY = desc2D.srcY;
  desc3D.srcZ = 0;
  desc3D.srcLOD = 0;
  desc3D.srcMemoryType = desc2D.srcMemoryType;
  desc3D.srcHost = desc2D.srcHost;
  desc3D.srcDevice = desc2D.srcDevice;
  desc3D.srcArray = desc2D.srcArray;
  desc3D.srcPitch = desc2D.srcPitch ? desc2D.srcPitch : (desc2D.srcXInBytes + desc2D.WidthInBytes);
  desc3D.srcHeight = 0;

  desc3D.dstXInBytes = desc2D.dstXInBytes;
  desc3D.dstY = desc2D.dstY;
  desc3D.dstZ = 0;
  desc3D.dstLOD = 0;
  desc3D.dstMemoryType = desc2D.dstMemoryType;
  desc3D.dstHost = desc2D.dstHost;
  desc3D.dstDevice = desc2D.dstDevice;
  desc3D.dstArray = desc2D.dstArray;
  desc3D.dstPitch = desc2D.dstPitch ? desc2D.dstPitch : (desc2D.dstXInBytes + desc2D.WidthInBytes);
  desc3D.dstHeight = 0;

  desc3D.WidthInBytes = desc2D.WidthInBytes;
  desc3D.Height = desc2D.Height;
  desc3D.Depth = 1;

  return desc3D;
}

inline HIP_MEMCPY3D getDrvMemcpy3DDesc(const hipMemcpy3DParms& desc) {
  HIP_MEMCPY3D descDrv = {};

  descDrv.WidthInBytes = desc.extent.width;
  descDrv.Height = desc.extent.height;
  descDrv.Depth = desc.extent.depth;

  descDrv.srcXInBytes = desc.srcPos.x;
  descDrv.srcY = desc.srcPos.y;
  descDrv.srcZ = desc.srcPos.z;
  descDrv.srcLOD = 0;

  descDrv.dstXInBytes = desc.dstPos.x;
  descDrv.dstY = desc.dstPos.y;
  descDrv.dstZ = desc.dstPos.z;
  descDrv.dstLOD = 0;

  bool is1DArraySrc = false;
  bool is1DArrayDst = false;

  if (desc.srcArray != nullptr) {
    descDrv.srcMemoryType = hipMemoryTypeArray;
    descDrv.srcArray = desc.srcArray;
    // When reffering to array memory, hipPos::x is in elements.
    descDrv.srcXInBytes *= getElementSize(desc.srcArray);
    is1DArraySrc = isLayered1D(descDrv.srcArray);
  }

  if (desc.srcPtr.ptr != nullptr) {
    descDrv.srcMemoryType = std::get<0>(hip::getMemoryType(desc.kind));
    descDrv.srcHost = desc.srcPtr.ptr;
    descDrv.srcDevice = desc.srcPtr.ptr;
    descDrv.srcPitch = desc.srcPtr.pitch;
    descDrv.srcHeight = desc.srcPtr.ysize;
  }

  if (desc.dstArray != nullptr) {
    descDrv.dstMemoryType = hipMemoryTypeArray;
    descDrv.dstArray = desc.dstArray;
    // When reffering to array memory, hipPos::x is in elements.
    descDrv.dstXInBytes *= getElementSize(desc.dstArray);
    is1DArrayDst = isLayered1D(descDrv.dstArray);
  }

  if (desc.dstPtr.ptr != nullptr) {
    descDrv.dstMemoryType = std::get<1>(getMemoryType(desc.kind));
    descDrv.dstHost = desc.dstPtr.ptr;
    descDrv.dstDevice = desc.dstPtr.ptr;
    descDrv.dstPitch = desc.dstPtr.pitch;
    descDrv.dstHeight = desc.dstPtr.ysize;
  }

  // If a HIP array is participating in the copy, the extent is defined in terms of that array's
  // elements.
  if ((desc.srcArray != nullptr) && (desc.dstArray == nullptr)) {
    descDrv.WidthInBytes *= getElementSize(desc.srcArray);
  } else if ((desc.srcArray == nullptr) && (desc.dstArray != nullptr)) {
    descDrv.WidthInBytes *= getElementSize(desc.dstArray);
  } else if ((desc.srcArray != nullptr) && (desc.dstArray != nullptr)) {
    descDrv.WidthInBytes *= getElementSize(desc.dstArray);
  }

  // The following will happen in
  // buffer to/from layered 1D, layered 1D to/from layered 1D
  size_t t = 0;
  if (is1DArraySrc) {
    t = descDrv.srcY;
    descDrv.srcY = descDrv.srcZ;
    descDrv.srcZ = t;
  }
  if (is1DArrayDst) {
    t = descDrv.dstY;
    descDrv.dstY = descDrv.dstZ;
    descDrv.dstZ = t;
  }

  if (is1DArraySrc || is1DArrayDst) {
    t = descDrv.Height;
    descDrv.Height = descDrv.Depth;
    descDrv.Depth = t;
  }
  return descDrv;
}

inline hipResourceType getResourceType(const HIPresourcetype resType) {
  // These two enums should be isomorphic.
  return static_cast<hipResourceType>(resType);
}

inline HIPresourcetype getResourceType(const hipResourceType resType) {
  // These two enums should be isomorphic.
  return static_cast<HIPresourcetype>(resType);
}

inline hipResourceDesc getResourceDesc(const HIP_RESOURCE_DESC& resDesc) {
  hipResourceDesc desc;

  desc.resType = getResourceType(resDesc.resType);
  switch (desc.resType) {
    case hipResourceTypeArray:
      desc.res.array.array = resDesc.res.array.hArray;
      break;
    case hipResourceTypeMipmappedArray:
      desc.res.mipmap.mipmap = resDesc.res.mipmap.hMipmappedArray;
      break;
    case hipResourceTypeLinear:
      desc.res.linear.devPtr = resDesc.res.linear.devPtr;
      desc.res.linear.desc =
          getChannelFormatDesc(resDesc.res.linear.numChannels, resDesc.res.linear.format);
      desc.res.linear.sizeInBytes = resDesc.res.linear.sizeInBytes;
      break;
    case hipResourceTypePitch2D:
      desc.res.pitch2D.devPtr = resDesc.res.pitch2D.devPtr;
      desc.res.pitch2D.desc =
          getChannelFormatDesc(resDesc.res.pitch2D.numChannels, resDesc.res.pitch2D.format);
      desc.res.pitch2D.width = resDesc.res.pitch2D.width;
      desc.res.pitch2D.height = resDesc.res.pitch2D.height;
      desc.res.pitch2D.pitchInBytes = resDesc.res.pitch2D.pitchInBytes;
      break;
    default:
      break;
  }

  return desc;
}

inline HIP_RESOURCE_DESC getResourceDesc(const hipResourceDesc& resDesc) {
  HIP_RESOURCE_DESC desc;

  desc.resType = getResourceType(resDesc.resType);
  switch (desc.resType) {
    case HIP_RESOURCE_TYPE_ARRAY:
      desc.res.array.hArray = resDesc.res.array.array;
      break;
    case HIP_RESOURCE_TYPE_MIPMAPPED_ARRAY:
      desc.res.mipmap.hMipmappedArray = resDesc.res.mipmap.mipmap;
      break;
    case HIP_RESOURCE_TYPE_LINEAR:
      desc.res.linear.devPtr = resDesc.res.linear.devPtr;
      desc.res.linear.numChannels = getNumChannels(resDesc.res.linear.desc);
      desc.res.linear.format = getArrayFormat(resDesc.res.linear.desc);
      desc.res.linear.sizeInBytes = resDesc.res.linear.sizeInBytes;
      break;
    case HIP_RESOURCE_TYPE_PITCH2D:
      desc.res.pitch2D.devPtr = resDesc.res.pitch2D.devPtr;
      desc.res.pitch2D.numChannels = getNumChannels(resDesc.res.pitch2D.desc);
      desc.res.pitch2D.format = getArrayFormat(resDesc.res.pitch2D.desc);
      desc.res.pitch2D.width = resDesc.res.pitch2D.width;
      desc.res.pitch2D.height = resDesc.res.pitch2D.height;
      desc.res.pitch2D.pitchInBytes = resDesc.res.pitch2D.pitchInBytes;
      break;
    default:
      break;
  }

  return desc;
}

inline hipTextureAddressMode getAddressMode(const HIPaddress_mode mode) {
  // These two enums should be isomorphic.
  return static_cast<hipTextureAddressMode>(mode);
}

inline HIPaddress_mode getAddressMode(const hipTextureAddressMode mode) {
  // These two enums should be isomorphic.
  return static_cast<HIPaddress_mode>(mode);
}

inline hipTextureFilterMode getFilterMode(const HIPfilter_mode mode) {
  // These two enums should be isomorphic.
  return static_cast<hipTextureFilterMode>(mode);
}

inline HIPfilter_mode getFilterMode(const hipTextureFilterMode mode) {
  // These two enums should be isomorphic.
  return static_cast<HIPfilter_mode>(mode);
}

inline hipTextureReadMode getReadMode(const unsigned int flags) {
  if (flags & HIP_TRSF_READ_AS_INTEGER) {
    return hipReadModeElementType;
  } else {
    return hipReadModeNormalizedFloat;
  }
}

inline unsigned int getReadMode(const hipTextureReadMode mode) {
  if (mode == hipReadModeElementType) {
    return HIP_TRSF_READ_AS_INTEGER;
  } else {
    return 0;
  }
}

inline int getsRGB(const unsigned int flags) {
  if (flags & HIP_TRSF_SRGB) {
    return 1;
  } else {
    return 0;
  }
}

inline unsigned int getsRGB(const int sRGB) {
  if (sRGB == 1) {
    return HIP_TRSF_SRGB;
  } else {
    return 0;
  }
}

inline int getNormalizedCoords(const unsigned int flags) {
  if (flags & HIP_TRSF_NORMALIZED_COORDINATES) {
    return 1;
  } else {
    return 0;
  }
}

inline unsigned int getNormalizedCoords(const int normalizedCoords) {
  if (normalizedCoords == 1) {
    return HIP_TRSF_NORMALIZED_COORDINATES;
  } else {
    return 0;
  }
}

inline hipTextureDesc getTextureDesc(const HIP_TEXTURE_DESC& texDesc) {
  hipTextureDesc desc;

  desc.addressMode[0] = getAddressMode(texDesc.addressMode[0]);
  desc.addressMode[1] = getAddressMode(texDesc.addressMode[1]);
  desc.addressMode[2] = getAddressMode(texDesc.addressMode[2]);
  desc.filterMode = getFilterMode(texDesc.filterMode);
  desc.readMode = getReadMode(texDesc.flags);
  desc.sRGB = getsRGB(texDesc.flags);
  std::memcpy(desc.borderColor, texDesc.borderColor, sizeof(desc.borderColor));
  desc.normalizedCoords = getNormalizedCoords(texDesc.flags);
  desc.maxAnisotropy = texDesc.maxAnisotropy;
  desc.mipmapFilterMode = getFilterMode(texDesc.mipmapFilterMode);
  desc.mipmapLevelBias = texDesc.mipmapLevelBias;
  desc.minMipmapLevelClamp = texDesc.minMipmapLevelClamp;
  desc.maxMipmapLevelClamp = texDesc.maxMipmapLevelClamp;

  return desc;
}

inline HIP_TEXTURE_DESC getTextureDesc(const hipTextureDesc& texDesc) {
  HIP_TEXTURE_DESC desc;

  desc.addressMode[0] = getAddressMode(texDesc.addressMode[0]);
  desc.addressMode[1] = getAddressMode(texDesc.addressMode[1]);
  desc.addressMode[2] = getAddressMode(texDesc.addressMode[2]);
  desc.filterMode = getFilterMode(texDesc.filterMode);
  desc.flags = 0;
  desc.flags |= getReadMode(texDesc.readMode);
  desc.flags |= getsRGB(texDesc.sRGB);
  desc.flags |= getNormalizedCoords(texDesc.normalizedCoords);
  desc.maxAnisotropy = texDesc.maxAnisotropy;
  desc.mipmapFilterMode = getFilterMode(texDesc.mipmapFilterMode);
  desc.mipmapLevelBias = texDesc.mipmapLevelBias;
  desc.minMipmapLevelClamp = texDesc.minMipmapLevelClamp;
  desc.maxMipmapLevelClamp = texDesc.maxMipmapLevelClamp;
  std::memcpy(desc.borderColor, texDesc.borderColor, sizeof(desc.borderColor));

  return desc;
}

inline hipResourceViewFormat getResourceViewFormat(const HIPresourceViewFormat format) {
  // These two enums should be isomorphic.
  return static_cast<hipResourceViewFormat>(format);
}

inline HIPresourceViewFormat getResourceViewFormat(const hipResourceViewFormat format) {
  // These two enums should be isomorphic.
  return static_cast<HIPresourceViewFormat>(format);
}

inline hipResourceViewDesc getResourceViewDesc(const HIP_RESOURCE_VIEW_DESC& resViewDesc) {
  hipResourceViewDesc desc;

  desc.format = getResourceViewFormat(resViewDesc.format);
  desc.width = resViewDesc.width;
  desc.height = resViewDesc.height;
  desc.depth = resViewDesc.depth;
  desc.firstMipmapLevel = resViewDesc.firstMipmapLevel;
  desc.lastMipmapLevel = resViewDesc.lastMipmapLevel;
  desc.firstLayer = resViewDesc.firstLayer;
  desc.lastLayer = resViewDesc.lastLayer;

  return desc;
}

inline HIP_RESOURCE_VIEW_DESC getResourceViewDesc(const hipResourceViewDesc& resViewDesc) {
  HIP_RESOURCE_VIEW_DESC desc;

  desc.format = getResourceViewFormat(resViewDesc.format);
  desc.width = resViewDesc.width;
  desc.height = resViewDesc.height;
  desc.depth = resViewDesc.depth;
  desc.firstMipmapLevel = resViewDesc.firstMipmapLevel;
  desc.lastMipmapLevel = resViewDesc.lastMipmapLevel;
  desc.firstLayer = resViewDesc.firstLayer;
  desc.lastLayer = resViewDesc.lastLayer;

  return desc;
}

inline size_t getElementSize(const hipChannelFormatDesc& desc) {
  return (desc.x / 8) * getNumChannels(desc);
}

inline hipMemcpy3DParms getMemcpy3DParms(const hipMemcpy3DBatchOp& desc) {
  hipMemcpy3DParms params;
  params.extent = desc.extent;
  params.kind = hipMemcpyDefault;

  // infer elementSize
  size_t elementSize = 1;
  if (desc.src.type == hipMemcpyOperandTypeArray) {
    elementSize = getElementSize(desc.src.op.array.array);
  } else if (desc.dst.type == hipMemcpyOperandTypeArray) {
    elementSize = getElementSize(desc.dst.op.array.array);
  }

  // source
  if (desc.src.type == hipMemcpyOperandTypePointer) {
    size_t row = desc.src.op.ptr.rowLength;
    size_t height = desc.src.op.ptr.layerHeight;
    size_t spitch = (row ? row : desc.extent.width) * elementSize;
    size_t swidth = (row ? row : desc.extent.width);
    size_t sheight = (height ? height : desc.extent.height);
    params.srcPtr = make_hipPitchedPtr(desc.src.op.ptr.ptr, spitch, swidth, sheight);
    params.srcPos = make_hipPos(0, 0, 0);
    params.srcArray = nullptr;
  } else if (desc.src.type == hipMemcpyOperandTypeArray) {
    params.srcArray = desc.src.op.array.array;
    params.srcPos = make_hipPos(desc.src.op.array.offset.x, desc.src.op.array.offset.y,
                                desc.src.op.array.offset.z);
    params.srcPtr = make_hipPitchedPtr(nullptr, 0, 0, 0);
  }
  // dest
  if (desc.dst.type == hipMemcpyOperandTypePointer) {
    size_t row = desc.dst.op.ptr.rowLength;
    size_t height = desc.dst.op.ptr.layerHeight;
    size_t spitch = (row ? row : desc.extent.width) * elementSize;
    size_t swidth = (row ? row : desc.extent.width);
    size_t sheight = (height ? height : desc.extent.height);
    params.dstPtr = make_hipPitchedPtr(desc.dst.op.ptr.ptr, spitch, swidth, sheight);
    params.dstPos = make_hipPos(0, 0, 0);
    params.dstArray = nullptr;
  } else if (desc.dst.type == hipMemcpyOperandTypeArray) {
    params.dstArray = desc.dst.op.array.array;
    params.dstPos = make_hipPos(desc.dst.op.array.offset.x, desc.dst.op.array.offset.y,
                                desc.dst.op.array.offset.z);
    params.dstPtr = make_hipPitchedPtr(nullptr, 0, 0, 0);
  }
  return params;
}

inline hipMemcpy3DParms getMemcpy3DParms(const hipMemcpy3DPeerParms& desc) {
  hipMemcpy3DParms params;
  params.srcArray = desc.srcArray;
  params.srcPos = desc.srcPos;
  params.srcPtr = desc.srcPtr;
  params.dstArray = desc.dstArray;
  params.dstPos = desc.dstPos;
  params.dstPtr = desc.dstPtr;
  params.extent = desc.extent;
  params.kind = hipMemcpyDeviceToDevice;
  return params;
}

inline hipArrayMemoryRequirements getArrayMemoryRequirements(
    const hipArrayMemoryRequirements& desc) {
  return hipArrayMemoryRequirements { desc.alignment, desc.size };
}
};  // namespace hip
