//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// CLImage.cpp: Implements the cl::Image class.
//

#include "CL/cl_half.h"
#include "common/unsafe_buffers.h"

#include "libANGLE/CLContext.h"
#include "libANGLE/CLImage.h"
#include "libANGLE/cl_utils.h"

#include <cstring>

namespace cl
{

bool Image::IsTypeValid(MemObjectType imageType)
{
    switch (imageType)
    {
        case MemObjectType::Image1D:
        case MemObjectType::Image2D:
        case MemObjectType::Image3D:
        case MemObjectType::Image1D_Array:
        case MemObjectType::Image2D_Array:
        case MemObjectType::Image1D_Buffer:
            break;
        default:
            return false;
    }
    return true;
}

angle::Result Image::getInfo(ImageInfo name,
                             size_t valueSize,
                             void *value,
                             size_t *valueSizeRet) const
{
    size_t valSizeT       = 0u;
    void *valPointer      = nullptr;
    const void *copyValue = nullptr;
    size_t copySize       = 0u;

    switch (name)
    {
        case ImageInfo::Format:
            copyValue = &mFormat;
            copySize  = sizeof(mFormat);
            break;
        case ImageInfo::ElementSize:
            valSizeT  = GetElementSize(mFormat);
            copyValue = &valSizeT;
            copySize  = sizeof(valSizeT);
            break;
        case ImageInfo::RowPitch:
            copyValue = &mDesc.rowPitch;
            copySize  = sizeof(mDesc.rowPitch);
            break;
        case ImageInfo::SlicePitch:
            copyValue = &mDesc.slicePitch;
            copySize  = sizeof(mDesc.slicePitch);
            break;
        case ImageInfo::Width:
            copyValue = &mDesc.width;
            copySize  = sizeof(mDesc.width);
            break;
        case ImageInfo::Height:
            copyValue = Is1DImage(mDesc.type) ? &valSizeT : &mDesc.height;
            copySize  = sizeof(mDesc.height);
            break;
        case ImageInfo::Depth:
            copyValue = Is3DImage(mDesc.type) ? &mDesc.depth : &valSizeT;
            copySize  = sizeof(mDesc.depth);
            break;
        case ImageInfo::ArraySize:
            copyValue = IsArrayType(mDesc.type) ? &mDesc.arraySize : &valSizeT;
            copySize  = sizeof(mDesc.arraySize);
            break;
        case ImageInfo::Buffer:
            valPointer = Memory::CastNative(mParent.get());
            copyValue  = &valPointer;
            copySize   = sizeof(valPointer);
            break;
        case ImageInfo::NumMipLevels:
            copyValue = &mDesc.numMipLevels;
            copySize  = sizeof(mDesc.numMipLevels);
            break;
        case ImageInfo::NumSamples:
            copyValue = &mDesc.numSamples;
            copySize  = sizeof(mDesc.numSamples);
            break;
        default:
            ANGLE_CL_RETURN_ERROR(CL_INVALID_VALUE);
    }

    if (value != nullptr)
    {
        // CL_INVALID_VALUE if size in bytes specified by param_value_size is < size of return type
        // as described in the Image Object Queries table and param_value is not NULL.
        if (valueSize < copySize)
        {
            ANGLE_CL_RETURN_ERROR(CL_INVALID_VALUE);
        }
        if (copyValue != nullptr)
        {
            ANGLE_UNSAFE_TODO(std::memcpy(value, copyValue, copySize));
        }
    }
    if (valueSizeRet != nullptr)
    {
        *valueSizeRet = copySize;
    }
    return angle::Result::Continue;
}

PixelColor Image::packPixels(const void *fillColor) const
{
    PixelColor packedColor;
    const cl_image_format &fmt = getFormat();
    const size_t channelCount  = GetChannelCount(fmt.image_channel_order);
    ChannelMapping swizzle     = GetChannelOrderMapping(fmt.image_channel_order);

    switch (fmt.image_channel_data_type)
    {
        case CL_UNORM_INT8:
        {
            const float *src = static_cast<const float *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                ANGLE_UNSAFE_TODO(packedColor.u8[i]) = gl::floatToNormalized<uint8_t>(
                    gl::clamp(ANGLE_UNSAFE_TODO(src[swizzle[i]]), 0.0f, 1.0f));
            }
            break;
        }
        case CL_UNORM_INT16:
        {
            const float *src = static_cast<const float *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                ANGLE_UNSAFE_TODO(packedColor.u16[i]) = gl::floatToNormalized<uint16_t>(
                    gl::clamp(ANGLE_UNSAFE_TODO(src[swizzle[i]]), 0.0f, 1.0f));
            }
            break;
        }
        case CL_SNORM_INT8:
        {
            const float *src = static_cast<const float *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                ANGLE_UNSAFE_TODO(packedColor.s8[i]) = gl::floatToNormalized<int8_t>(
                    gl::clamp(ANGLE_UNSAFE_TODO(src[swizzle[i]]), -1.0f, 1.0f));
            }
            break;
        }
        case CL_SNORM_INT16:
        {
            const float *src = static_cast<const float *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                ANGLE_UNSAFE_TODO(packedColor.s16[i]) = gl::floatToNormalized<int16_t>(
                    gl::clamp(ANGLE_UNSAFE_TODO(src[swizzle[i]]), -1.0f, 1.0f));
            }
            break;
        }
        case CL_SIGNED_INT8:
        case CL_SIGNED_INT16:
        case CL_SIGNED_INT32:
        {
            const int *src = static_cast<const int *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                if (fmt.image_channel_data_type == CL_SIGNED_INT8)
                {
                    ANGLE_UNSAFE_TODO(packedColor.s8[i] = gl::clampCast<int8_t>(src[swizzle[i]]));
                }
                else if (fmt.image_channel_data_type == CL_SIGNED_INT16)
                {
                    ANGLE_UNSAFE_TODO(packedColor.s16[i] = gl::clampCast<int16_t>(src[swizzle[i]]));
                }
                else
                {
                    ANGLE_UNSAFE_TODO(packedColor.s32[i] = src[swizzle[i]]);
                }
            }
            break;
        }
        case CL_UNSIGNED_INT8:
        case CL_UNSIGNED_INT16:
        case CL_UNSIGNED_INT32:
        {
            const unsigned *src = static_cast<const unsigned *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                if (fmt.image_channel_data_type == CL_UNSIGNED_INT8)
                {
                    ANGLE_UNSAFE_TODO(packedColor.u8[i] = gl::clampCast<uint8_t>(src[swizzle[i]]));
                }
                else if (fmt.image_channel_data_type == CL_UNSIGNED_INT16)
                {
                    ANGLE_UNSAFE_TODO(packedColor.u16[i] =
                                          gl::clampCast<uint16_t>(src[swizzle[i]]));
                }
                else
                {
                    ANGLE_UNSAFE_TODO(packedColor.u32[i] = src[swizzle[i]]);
                }
            }
            break;
        }
        case CL_HALF_FLOAT:
        {
            const float *src = static_cast<const float *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                ANGLE_UNSAFE_TODO(packedColor.fp16[i] =
                                      cl_half_from_float(src[swizzle[i]], CL_HALF_RTE));
            }
            break;
        }
        case CL_FLOAT:
        {
            const float *src = static_cast<const float *>(fillColor);
            for (size_t i = 0; i < channelCount; ++i)
            {
                ANGLE_UNSAFE_TODO(packedColor.fp32[i] = src[swizzle[i]]);
            }
            break;
        }
        default:
            UNIMPLEMENTED();
            break;
    }
    return packedColor;
}

Image::~Image() = default;

bool Image::isRegionValid(const cl::Offset &origin, const cl::Extents &region) const
{
    switch (getType())
    {
        case MemObjectType::Image1D:
        case MemObjectType::Image1D_Buffer:
            return origin.x + region.width <= mDesc.width;
        case MemObjectType::Image2D:
            return origin.x + region.width <= mDesc.width &&
                   origin.y + region.height <= mDesc.height;
        case MemObjectType::Image3D:
            return origin.x + region.width <= mDesc.width &&
                   origin.y + region.height <= mDesc.height &&
                   origin.z + region.depth <= mDesc.depth;
        case MemObjectType::Image1D_Array:
            return origin.x + region.width <= mDesc.width &&
                   origin.y + region.height <= mDesc.arraySize;
        case MemObjectType::Image2D_Array:
            return origin.x + region.width <= mDesc.width &&
                   origin.y + region.height <= mDesc.height &&
                   origin.z + region.depth <= mDesc.arraySize;
        default:
            ASSERT(false);
            break;
    }
    return false;
}

Image::Image(Context &context,
             PropArray &&properties,
             MemFlags flags,
             const cl_image_format &format,
             const ImageDescriptor &desc,
             Memory *parent,
             void *hostPtr)
    : Memory(context, std::move(properties), flags, parent, hostPtr), mFormat(format), mDesc(desc)
{
    mSize = getSliceSize() * getDepth() * getArraySize();
    ANGLE_CL_IMPL_TRY(context.getImpl().createImage(*this, hostPtr, &mImpl));
}

}  // namespace cl
