//
// Copyright 2002 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.
//

// utilities.h: Conversion functions and other utility routines.

#ifndef COMMON_UTILITIES_H_
#define COMMON_UTILITIES_H_

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLSLANG/ShaderLang.h>

#include <math.h>
#include <string>
#include <vector>

#include "angle_gl.h"

#include "common/PackedEnums.h"
#include "common/mathutil.h"
#include "common/platform.h"

namespace sh
{
struct ShaderVariable;
}

namespace gl
{

int VariableComponentCount(GLenum type);
GLenum VariableComponentType(GLenum type);
size_t VariableComponentSize(GLenum type);
size_t VariableInternalSize(GLenum type);
size_t VariableExternalSize(GLenum type);
int VariableRowCount(GLenum type);
int VariableColumnCount(GLenum type);
bool IsSamplerType(GLenum type);
bool IsSamplerCubeType(GLenum type);
bool IsSamplerYUVType(GLenum type);
bool IsImageType(GLenum type);
bool IsImage2DType(GLenum type);
bool IsAtomicCounterType(GLenum type);
bool IsOpaqueType(GLenum type);
bool IsMatrixType(GLenum type);
GLenum TransposeMatrixType(GLenum type);
int VariableRegisterCount(GLenum type);
int MatrixRegisterCount(GLenum type, bool isRowMajorMatrix);
int MatrixComponentCount(GLenum type, bool isRowMajorMatrix);
int VariableSortOrder(GLenum type);
GLenum VariableBoolVectorType(GLenum type);
std::string GetGLSLTypeString(GLenum type);

int AllocateFirstFreeBits(unsigned int *bits, unsigned int allocationSize, unsigned int bitsSize);

// Parse the base resource name and array indices. Returns the base name of the resource.
// If the provided name doesn't index an array, the outSubscripts vector will be empty.
// If the provided name indexes an array, the outSubscripts vector will contain indices with
// outermost array indices in the back. If an array index is invalid, GL_INVALID_INDEX is added to
// outSubscripts.
std::string ParseResourceName(const std::string &name, std::vector<unsigned int> *outSubscripts);

bool IsBuiltInName(const char *name);
ANGLE_INLINE bool IsBuiltInName(const std::string &name)
{
    return IsBuiltInName(name.c_str());
}

// Strips only the last array index from a resource name.
std::string StripLastArrayIndex(const std::string &name);

bool SamplerNameContainsNonZeroArrayElement(const std::string &name);

// Find the range of index values in the provided indices pointer.  Primitive restart indices are
// only counted in the range if primitive restart is disabled.
IndexRange ComputeIndexRange(DrawElementsType indexType,
                             const GLvoid *indices,
                             size_t count,
                             bool primitiveRestartEnabled);

// Get the primitive restart index value for the given index type.
GLuint GetPrimitiveRestartIndex(DrawElementsType indexType);

// Get the primitive restart index value with the given C++ type.
template <typename T>
constexpr T GetPrimitiveRestartIndexFromType()
{
    return std::numeric_limits<T>::max();
}

static_assert(GetPrimitiveRestartIndexFromType<uint8_t>() == 0xFF,
              "verify restart index for uint8_t values");
static_assert(GetPrimitiveRestartIndexFromType<uint16_t>() == 0xFFFF,
              "verify restart index for uint8_t values");
static_assert(GetPrimitiveRestartIndexFromType<uint32_t>() == 0xFFFFFFFF,
              "verify restart index for uint8_t values");

bool IsTriangleMode(PrimitiveMode drawMode);
bool IsPolygonMode(PrimitiveMode mode);

namespace priv
{
extern const angle::PackedEnumMap<PrimitiveMode, bool> gLineModes;
}  // namespace priv

ANGLE_INLINE bool IsLineMode(PrimitiveMode primitiveMode)
{
    return priv::gLineModes[primitiveMode];
}

bool IsIntegerFormat(GLenum unsizedFormat);

// Returns the product of the sizes in the vector, or 1 if the vector is empty. Doesn't currently
// perform overflow checks.
unsigned int ArraySizeProduct(const std::vector<unsigned int> &arraySizes);
// Returns the product of the sizes in the vector except for the outermost dimension, or 1 if the
// vector is empty.
unsigned int InnerArraySizeProduct(const std::vector<unsigned int> &arraySizes);
// Returns the outermost array dimension, or 1 if the vector is empty.
unsigned int OutermostArraySize(const std::vector<unsigned int> &arraySizes);

// Return the array index at the end of name, and write the length of name before the final array
// index into nameLengthWithoutArrayIndexOut. In case name doesn't include an array index, return
// GL_INVALID_INDEX and write the length of the original string.
unsigned int ParseArrayIndex(const std::string &name, size_t *nameLengthWithoutArrayIndexOut);

enum class SamplerFormat : uint8_t
{
    Float    = 0,
    Unsigned = 1,
    Signed   = 2,
    Shadow   = 3,

    InvalidEnum = 4,
    EnumCount   = 4,
};

struct UniformTypeInfo final : angle::NonCopyable
{
    inline constexpr UniformTypeInfo(GLenum type,
                                     GLenum componentType,
                                     GLenum textureType,
                                     GLenum transposedMatrixType,
                                     GLenum boolVectorType,
                                     SamplerFormat samplerFormat,
                                     int rowCount,
                                     int columnCount,
                                     int componentCount,
                                     size_t componentSize,
                                     size_t internalSize,
                                     size_t externalSize,
                                     bool isSampler,
                                     bool isMatrixType,
                                     bool isImageType);

    GLenum type;
    GLenum componentType;
    GLenum textureType;
    GLenum transposedMatrixType;
    GLenum boolVectorType;
    SamplerFormat samplerFormat;
    int rowCount;
    int columnCount;
    int componentCount;
    size_t componentSize;
    size_t internalSize;
    size_t externalSize;
    bool isSampler;
    bool isMatrixType;
    bool isImageType;
};

inline constexpr UniformTypeInfo::UniformTypeInfo(GLenum type,
                                                  GLenum componentType,
                                                  GLenum textureType,
                                                  GLenum transposedMatrixType,
                                                  GLenum boolVectorType,
                                                  SamplerFormat samplerFormat,
                                                  int rowCount,
                                                  int columnCount,
                                                  int componentCount,
                                                  size_t componentSize,
                                                  size_t internalSize,
                                                  size_t externalSize,
                                                  bool isSampler,
                                                  bool isMatrixType,
                                                  bool isImageType)
    : type(type),
      componentType(componentType),
      textureType(textureType),
      transposedMatrixType(transposedMatrixType),
      boolVectorType(boolVectorType),
      samplerFormat(samplerFormat),
      rowCount(rowCount),
      columnCount(columnCount),
      componentCount(componentCount),
      componentSize(componentSize),
      internalSize(internalSize),
      externalSize(externalSize),
      isSampler(isSampler),
      isMatrixType(isMatrixType),
      isImageType(isImageType)
{}

struct UniformTypeIndex
{
    uint16_t value;
};
const UniformTypeInfo &GetUniformTypeInfo(GLenum uniformType);
UniformTypeIndex GetUniformTypeIndex(GLenum uniformType);
const UniformTypeInfo &GetUniformTypeInfoFromIndex(UniformTypeIndex index);

const char *GetGenericErrorMessage(GLenum error);

unsigned int ElementTypeSize(GLenum elementType);

bool IsMipmapFiltered(GLenum minFilterMode);

template <typename T>
T GetClampedVertexCount(size_t vertexCount)
{
    static constexpr size_t kMax = static_cast<size_t>(std::numeric_limits<T>::max());
    return static_cast<T>(vertexCount > kMax ? kMax : vertexCount);
}

enum class PipelineType
{
    GraphicsPipeline = 0,
    ComputePipeline  = 1,
};

PipelineType GetPipelineType(ShaderType shaderType);

// For use with KHR_debug.
const char *GetDebugMessageSourceString(GLenum source);
const char *GetDebugMessageTypeString(GLenum type);
const char *GetDebugMessageSeverityString(GLenum severity);

// For use with EXT_texture_sRGB_decode
// A texture may be forced to skip decoding to a linear colorspace even if its format
// is in sRGB colorspace.
//
// Default - decode data according to the image's format's colorspace
// Skip - data is not decoded during sampling
enum class SrgbDecode
{
    Default = 0,
    Skip
};

// For use with EXT_texture_format_sRGB_override
// A texture may be forced to decode data to linear colorspace even if its format
// is in linear colorspace.
//
// Default - decode data according to the image's format's colorspace
// SRGB - data will be decoded to linear colorspace irrespective of texture's format
enum class SrgbOverride
{
    Default = 0,
    SRGB
};

// For use with EXT_sRGB_write_control
// A framebuffer may be forced to not encode data to sRGB colorspace even if its format
// is in sRGB colorspace.
//
// Default - encode data according to the image's format's colorspace
// Linear - data will not be encoded into sRGB colorspace
enum class SrgbWriteControlMode
{
    Default = 0,
    Linear  = 1
};

// For use with EXT_YUV_target
// A sampler of external YUV textures may either implicitly perform RGB conversion (regular
// samplerExternalOES) or skip the conversion and sample raw YUV values (__samplerExternal2DY2Y).
enum class YuvSamplingMode
{
    Default = 0,
    Y2Y     = 1
};

ShaderType GetShaderTypeFromBitfield(size_t singleShaderType);
GLbitfield GetBitfieldFromShaderType(ShaderType shaderType);
bool ShaderTypeSupportsTransformFeedback(ShaderType shaderType);
// Given a set of shader stages, returns the last vertex processing stage.  This is the stage that
// interfaces the fragment shader.
ShaderType GetLastPreFragmentStage(ShaderBitSet shaderTypes);

}  // namespace gl

namespace egl
{
// For use with EGL_EXT_image_gl_colorspace
// An EGLImage can be created with attributes that override the color space of underlying image data
// when rendering to the image, or sampling from the image. The possible values are -
//  Default - EGLImage source's colorspace should be preserved
//  sRGB - EGLImage targets will assume sRGB colorspace
//  Linear - EGLImage targets will assume linear colorspace
enum class ImageColorspace
{
    Default = 0,
    SRGB,
    Linear
};

static const EGLenum FirstCubeMapTextureTarget = EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR;
static const EGLenum LastCubeMapTextureTarget  = EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR;
bool IsCubeMapTextureTarget(EGLenum target);
size_t CubeMapTextureTargetToLayerIndex(EGLenum target);
EGLenum LayerIndexToCubeMapTextureTarget(size_t index);
bool IsTextureTarget(EGLenum target);
bool IsRenderbufferTarget(EGLenum target);
bool IsExternalImageTarget(EGLenum target);

const char *GetGenericErrorMessage(EGLint error);
}  // namespace egl

namespace egl_gl
{
GLuint EGLClientBufferToGLObjectHandle(EGLClientBuffer buffer);
}

namespace gl_egl
{
EGLenum GLComponentTypeToEGLColorComponentType(GLenum glComponentType);
EGLClientBuffer GLObjectHandleToEGLClientBuffer(GLuint handle);
}  // namespace gl_egl

namespace angle
{

// All state that modify attachment's colorspace
struct ColorspaceState
{
  public:
    ColorspaceState() { reset(); }
    void reset()
    {
        hasStaticTexelFetchAccess = false;
        srgbDecode                = gl::SrgbDecode::Default;
        srgbOverride              = gl::SrgbOverride::Default;
        srgbWriteControl          = gl::SrgbWriteControlMode::Default;
        eglImageColorspace        = egl::ImageColorspace::Default;
    }

    // States that affect read operations
    bool hasStaticTexelFetchAccess;
    gl::SrgbDecode srgbDecode;
    gl::SrgbOverride srgbOverride;

    // States that affect write operations
    gl::SrgbWriteControlMode srgbWriteControl;

    // States that affect both read and write operations
    egl::ImageColorspace eglImageColorspace;
};

template <typename T>
constexpr size_t ConstStrLen(T s)
{
    if (s == nullptr)
    {
        return 0;
    }
    return std::char_traits<char>::length(s);
}

bool IsDrawEntryPoint(EntryPoint entryPoint);
bool IsDispatchEntryPoint(EntryPoint entryPoint);
bool IsClearEntryPoint(EntryPoint entryPoint);
bool IsQueryEntryPoint(EntryPoint entryPoint);

template <typename T>
void FillWithNullptr(T *array)
{
    // std::array::fill(nullptr) yields unoptimized, unrolled loop over array items
    memset(array->data(), 0, array->size() * sizeof(*array->data()));
    // sanity check for non-0 nullptr
    ASSERT(array->data()[0] == nullptr);
}
}  // namespace angle

void writeFile(const char *path, const void *data, size_t size);

// Get the underlying type. Useful for indexing into arrays with enum values by avoiding the clutter
// of the extraneous static_cast<>() calls.
// https://stackoverflow.com/a/8357462
template <typename E>
constexpr typename std::underlying_type<E>::type ToUnderlying(E e) noexcept
{
    return static_cast<typename std::underlying_type<E>::type>(e);
}

#endif  // COMMON_UTILITIES_H_
