//
// Copyright (c) 2017 The Khronos Group Inc.
// 
// 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
//
// 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.
//
#ifndef _imageHelpers_h
#define _imageHelpers_h

#include "compat.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#if !defined(_WIN32)
#include <unistd.h>
#endif

#include <time.h>

#include "errorHelpers.h"

#include "conversions.h"
#include "typeWrappers.h"
#include "kernelHelpers.h"
#include "errorHelpers.h"
#include "mt19937.h"
#include "rounding_mode.h"
#include "clImageHelper.h"

extern int gTestCount;
extern int gTestFailure;
extern cl_device_type gDeviceType;

// Number of iterations per image format to test if not testing max images, rounding, or small images
#define NUM_IMAGE_ITERATIONS 3


// Definition for our own sampler type, to mirror the cl_sampler internals
#define MAX_sRGB_TO_lRGB_CONVERSION_ERROR 0.5
#define MAX_lRGB_TO_sRGB_CONVERSION_ERROR 0.6

// Definition for our own sampler type, to mirror the cl_sampler internals
typedef struct {
 cl_addressing_mode addressing_mode;
 cl_filter_mode     filter_mode;
 bool               normalized_coords;
} image_sampler_data;

int round_to_even( float v );

#define NORMALIZE( v, max ) ( v < 0 ? 0 : ( v > 1.f ? max : round_to_even( v * max ) ) )
#define NORMALIZE_UNROUNDED( v, max ) ( v < 0 ? 0 : ( v > 1.f ? max :  v * max ) )
#define NORMALIZE_SIGNED( v, min, max ) ( v  < -1.0f ? min : ( v > 1.f ? max : round_to_even( v * max ) ) )
#define NORMALIZE_SIGNED_UNROUNDED( v, min, max ) ( v  < -1.0f ? min : ( v > 1.f ? max : v * max ) )
#define CONVERT_INT( v, min, max, max_val)  ( v < min ? min : ( v > max ? max_val : round_to_even( v ) ) )
#define CONVERT_UINT( v, max, max_val)  ( v < 0 ? 0 : ( v > max ? max_val : round_to_even( v ) ) )

extern void print_read_header( cl_image_format *format, image_sampler_data *sampler, bool err = false, int t = 0 );
extern void print_write_header( cl_image_format *format, bool err);
extern void print_header( cl_image_format *format, bool err );
extern bool find_format( cl_image_format *formatList, unsigned int numFormats, cl_image_format *formatToFind );
extern bool check_minimum_supported( cl_image_format *formatList, unsigned int numFormats, cl_mem_flags flags );

extern size_t get_format_type_size( const cl_image_format *format );
extern size_t get_channel_data_type_size( cl_channel_type channelType );
extern size_t get_format_channel_count( const cl_image_format *format );
extern size_t get_channel_order_channel_count( cl_channel_order order );
cl_channel_type  get_channel_type_from_name( const char *name );
cl_channel_order  get_channel_order_from_name( const char *name );
extern int    is_format_signed( const cl_image_format *format );
extern size_t get_pixel_size( cl_image_format *format );

/* Helper to get any ol image format as long as it is 8-bits-per-channel */
extern int get_8_bit_image_format( cl_context context, cl_mem_object_type objType, cl_mem_flags flags, size_t channelCount, cl_image_format *outFormat );

/* Helper to get any ol image format as long as it is 32-bits-per-channel */
extern int get_32_bit_image_format( cl_context context, cl_mem_object_type objType, cl_mem_flags flags, size_t channelCount, cl_image_format *outFormat );

int random_in_range( int minV, int maxV, MTdata d );
int random_log_in_range( int minV, int maxV, MTdata d );

typedef struct
{
    size_t width;
    size_t height;
    size_t depth;
    size_t rowPitch;
    size_t slicePitch;
    size_t arraySize;
    cl_image_format *format;
    cl_mem buffer;
    cl_mem_object_type type;
    cl_uint num_mip_levels;
} image_descriptor;

typedef struct
{
    float p[4];
}FloatPixel;

void get_max_sizes(size_t *numberOfSizes, const int maxNumberOfSizes,
                   size_t sizes[][3], size_t maxWidth, size_t maxHeight, size_t maxDepth, size_t maxArraySize,
                   const cl_ulong maxIndividualAllocSize, const cl_ulong maxTotalAllocSize, cl_mem_object_type image_type, cl_image_format *format, int usingMaxPixelSize=0);
extern size_t get_format_max_int( cl_image_format *format );

extern cl_ulong get_image_size( image_descriptor const *imageInfo );
extern cl_ulong get_image_size_mb( image_descriptor const *imageInfo );

extern char * generate_random_image_data( image_descriptor *imageInfo, BufferOwningPtr<char> &Owner, MTdata d );

extern int debug_find_vector_in_image( void *imagePtr, image_descriptor *imageInfo,
                                      void *vectorToFind, size_t vectorSize, int *outX, int *outY, int *outZ, size_t lod = 0 );

extern int debug_find_pixel_in_image( void *imagePtr, image_descriptor *imageInfo,
                                     unsigned int *valuesToFind, int *outX, int *outY, int *outZ, int lod = 0 );
extern int debug_find_pixel_in_image( void *imagePtr, image_descriptor *imageInfo,
                                     int *valuesToFind, int *outX, int *outY, int *outZ, int lod = 0 );
extern int debug_find_pixel_in_image( void *imagePtr, image_descriptor *imageInfo,
                                     float *valuesToFind, int *outX, int *outY, int *outZ, int lod = 0 );

extern void copy_image_data( image_descriptor *srcImageInfo, image_descriptor *dstImageInfo, void *imageValues, void *destImageValues,
                            const size_t sourcePos[], const size_t destPos[], const size_t regionSize[] );

int has_alpha(cl_image_format *format);

extern bool is_sRGBA_order(cl_channel_order image_channel_order);

inline float calculate_array_index( float coord, float extent );

cl_uint compute_max_mip_levels( size_t width, size_t height, size_t depth);
cl_ulong compute_mipmapped_image_size( image_descriptor imageInfo);
size_t compute_mip_level_offset( image_descriptor * imageInfo , size_t lod);

template <class T> void read_image_pixel( void *imageData, image_descriptor *imageInfo,
                                         int x, int y, int z, T *outData, int lod )
{
    float convert_half_to_float( unsigned short halfValue );
    size_t width_lod = imageInfo->width, height_lod = imageInfo->height, depth_lod = imageInfo->depth, slice_pitch_lod = 0/*imageInfo->slicePitch*/ , row_pitch_lod = 0/*imageInfo->rowPitch*/;
    width_lod = ( imageInfo->width >> lod) ?( imageInfo->width >> lod):1;

    if ( imageInfo->type  != CL_MEM_OBJECT_IMAGE1D_ARRAY && imageInfo->type != CL_MEM_OBJECT_IMAGE1D)
        height_lod = ( imageInfo->height >> lod) ?( imageInfo->height >> lod):1;

    if(imageInfo->type == CL_MEM_OBJECT_IMAGE3D)
       depth_lod = ( imageInfo->depth >> lod) ? ( imageInfo->depth >> lod) : 1;
    row_pitch_lod = (imageInfo->num_mip_levels > 0)? (width_lod * get_pixel_size( imageInfo->format )): imageInfo->rowPitch;
    slice_pitch_lod = (imageInfo->num_mip_levels > 0)? (row_pitch_lod * height_lod): imageInfo->slicePitch;

    // correct depth_lod and height_lod for array image types in order to avoid
    // return
    if (imageInfo->type == CL_MEM_OBJECT_IMAGE1D_ARRAY && height_lod == 1 && depth_lod == 1) {
    depth_lod = 0;
    height_lod = 0;

    }

    if (imageInfo->type == CL_MEM_OBJECT_IMAGE2D_ARRAY && depth_lod == 1) {
      depth_lod = 0;
    }

    if ( x < 0 || x >= (int)width_lod
               || ( height_lod != 0 && ( y < 0 || y >= (int)height_lod ) )
               || ( depth_lod != 0 && ( z < 0 || z >= (int)depth_lod ) )
               || ( imageInfo->arraySize != 0 && ( z < 0 || z >= (int)imageInfo->arraySize ) ) )
    {
        // Border color
        if (imageInfo->format->image_channel_order == CL_DEPTH)
        {
            outData[ 0 ] = 1;
        }
        else {
            outData[ 0 ] = outData[ 1 ] = outData[ 2 ] = outData[ 3 ] = 0;
            if (!has_alpha(imageInfo->format))
                outData[3] = 1;
        }
        return;
    }

    cl_image_format *format = imageInfo->format;

    unsigned int i;
    T tempData[ 4 ];

    // Advance to the right spot
    char *ptr = (char *)imageData;
    size_t pixelSize = get_pixel_size( format );

    ptr += z * slice_pitch_lod + y * row_pitch_lod + x * pixelSize;

    // OpenCL only supports reading floats from certain formats
    switch( format->image_channel_data_type )
    {
        case CL_SNORM_INT8:
        {
            cl_char *dPtr = (cl_char *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_UNORM_INT8:
        {
            cl_uchar *dPtr = (cl_uchar *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_SIGNED_INT8:
        {
            cl_char *dPtr = (cl_char *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_UNSIGNED_INT8:
        {
            cl_uchar *dPtr = (cl_uchar*)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_SNORM_INT16:
        {
            cl_short *dPtr = (cl_short *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_UNORM_INT16:
        {
            cl_ushort *dPtr = (cl_ushort *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_SIGNED_INT16:
        {
            cl_short *dPtr = (cl_short *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_UNSIGNED_INT16:
        {
            cl_ushort *dPtr = (cl_ushort *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_HALF_FLOAT:
        {
            cl_ushort *dPtr = (cl_ushort *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)convert_half_to_float( dPtr[ i ] );
            break;
        }

        case CL_SIGNED_INT32:
        {
            cl_int *dPtr = (cl_int *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_UNSIGNED_INT32:
        {
            cl_uint *dPtr = (cl_uint *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }

        case CL_UNORM_SHORT_565:
        {
            cl_ushort *dPtr = (cl_ushort*)ptr;
            tempData[ 0 ] = (T)( dPtr[ 0 ] >> 11 );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 5 ) & 63 );
            tempData[ 2 ] = (T)( dPtr[ 0 ] & 31 );
            break;
        }

#ifdef OBSOLETE_FORMAT
        case CL_UNORM_SHORT_565_REV:
        {
            unsigned short *dPtr = (unsigned short *)ptr;
            tempData[ 2 ] = (T)( dPtr[ 0 ] >> 11 );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 5 ) & 63 );
            tempData[ 0 ] = (T)( dPtr[ 0 ] & 31 );
            break;
        }

        case CL_UNORM_SHORT_555_REV:
        {
            unsigned short *dPtr = (unsigned short *)ptr;
            tempData[ 2 ] = (T)( ( dPtr[ 0 ] >> 10 ) & 31 );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 5 ) & 31 );
            tempData[ 0 ] = (T)( dPtr[ 0 ] & 31 );
            break;
        }

        case CL_UNORM_INT_8888:
        {
            unsigned int *dPtr = (unsigned int *)ptr;
            tempData[ 3 ] = (T)( dPtr[ 0 ] >> 24 );
            tempData[ 2 ] = (T)( ( dPtr[ 0 ] >> 16 ) & 0xff );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 8 ) & 0xff );
            tempData[ 0 ] = (T)( dPtr[ 0 ] & 0xff );
            break;
        }
        case CL_UNORM_INT_8888_REV:
        {
            unsigned int *dPtr = (unsigned int *)ptr;
            tempData[ 0 ] = (T)( dPtr[ 0 ] >> 24 );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 16 ) & 0xff );
            tempData[ 2 ] = (T)( ( dPtr[ 0 ] >> 8 ) & 0xff );
            tempData[ 3 ] = (T)( dPtr[ 0 ] & 0xff );
            break;
        }

        case CL_UNORM_INT_101010_REV:
        {
            unsigned int *dPtr = (unsigned int *)ptr;
            tempData[ 2 ] = (T)( ( dPtr[ 0 ] >> 20 ) & 0x3ff );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 10 ) & 0x3ff );
            tempData[ 0 ] = (T)( dPtr[ 0 ] & 0x3ff );
            break;
        }
#endif
        case CL_UNORM_SHORT_555:
        {
            cl_ushort *dPtr = (cl_ushort *)ptr;
            tempData[ 0 ] = (T)( ( dPtr[ 0 ] >> 10 ) & 31 );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 5 ) & 31 );
            tempData[ 2 ] = (T)( dPtr[ 0 ] & 31 );
            break;
        }

        case CL_UNORM_INT_101010:
        {
            cl_uint *dPtr = (cl_uint *)ptr;
            tempData[ 0 ] = (T)( ( dPtr[ 0 ] >> 20 ) & 0x3ff );
            tempData[ 1 ] = (T)( ( dPtr[ 0 ] >> 10 ) & 0x3ff );
            tempData[ 2 ] = (T)( dPtr[ 0 ] & 0x3ff );
            break;
        }

        case CL_FLOAT:
        {
            cl_float *dPtr = (cl_float *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ];
            break;
        }
#ifdef CL_SFIXED14_APPLE
        case CL_SFIXED14_APPLE:
        {
            cl_float *dPtr = (cl_float *)ptr;
            for( i = 0; i < get_format_channel_count( format ); i++ )
                tempData[ i ] = (T)dPtr[ i ] + 0x4000;
            break;
        }
#endif
    }


    outData[ 0 ] = outData[ 1 ] = outData[ 2 ] = 0;
    outData[ 3 ] = 1;

    if( format->image_channel_order == CL_A )
    {
        outData[ 3 ] = tempData[ 0 ];
    }
    else if( format->image_channel_order == CL_R   )
    {
        outData[ 0 ] = tempData[ 0 ];
    }
    else if( format->image_channel_order == CL_Rx   )
    {
        outData[ 0 ] = tempData[ 0 ];
    }
    else if( format->image_channel_order == CL_RA )
    {
        outData[ 0 ] = tempData[ 0 ];
        outData[ 3 ] = tempData[ 1 ];
    }
    else if( format->image_channel_order == CL_RG  )
    {
        outData[ 0 ] = tempData[ 0 ];
        outData[ 1 ] = tempData[ 1 ];
    }
    else if( format->image_channel_order == CL_RGx  )
    {
        outData[ 0 ] = tempData[ 0 ];
        outData[ 1 ] = tempData[ 1 ];
    }
    else if(( format->image_channel_order == CL_RGB  ) || ( format->image_channel_order == CL_sRGB  ))
    {
        outData[ 0 ] = tempData[ 0 ];
        outData[ 1 ] = tempData[ 1 ];
        outData[ 2 ] = tempData[ 2 ];
    }
    else if(( format->image_channel_order == CL_RGBx  ) || ( format->image_channel_order == CL_sRGBx  ))
    {
        outData[ 0 ] = tempData[ 0 ];
        outData[ 1 ] = tempData[ 1 ];
        outData[ 2 ] = tempData[ 2 ];
        outData[ 3 ] = 0;
    }
    else if(( format->image_channel_order == CL_RGBA ) || ( format->image_channel_order == CL_sRGBA ))
    {
        outData[ 0 ] = tempData[ 0 ];
        outData[ 1 ] = tempData[ 1 ];
        outData[ 2 ] = tempData[ 2 ];
        outData[ 3 ] = tempData[ 3 ];
    }
    else if( format->image_channel_order == CL_ARGB )
    {
        outData[ 0 ] = tempData[ 1 ];
        outData[ 1 ] = tempData[ 2 ];
        outData[ 2 ] = tempData[ 3 ];
        outData[ 3 ] = tempData[ 0 ];
    }
    else if(( format->image_channel_order == CL_BGRA ) || ( format->image_channel_order == CL_sBGRA ))
    {
        outData[ 0 ] = tempData[ 2 ];
        outData[ 1 ] = tempData[ 1 ];
        outData[ 2 ] = tempData[ 0 ];
        outData[ 3 ] = tempData[ 3 ];
    }
    else if( format->image_channel_order == CL_INTENSITY )
    {
        outData[ 1 ] = tempData[ 0 ];
        outData[ 2 ] = tempData[ 0 ];
        outData[ 3 ] = tempData[ 0 ];
    }
    else if( format->image_channel_order == CL_LUMINANCE )
    {
        outData[ 1 ] = tempData[ 0 ];
        outData[ 2 ] = tempData[ 0 ];
    }
    else if( format->image_channel_order == CL_DEPTH  )
    {
        outData[ 0 ] = tempData[ 0 ];
    }
#ifdef CL_1RGB_APPLE
    else if( format->image_channel_order == CL_1RGB_APPLE )
    {
        outData[ 0 ] = tempData[ 1 ];
        outData[ 1 ] = tempData[ 2 ];
        outData[ 2 ] = tempData[ 3 ];
        outData[ 3 ] = 0xff;
    }
#endif
#ifdef CL_BGR1_APPLE
    else if( format->image_channel_order == CL_BGR1_APPLE )
    {
        outData[ 0 ] = tempData[ 2 ];
        outData[ 1 ] = tempData[ 1 ];
        outData[ 2 ] = tempData[ 0 ];
        outData[ 3 ] = 0xff;
    }
#endif
    else
    {
        log_error("Invalid format:");
        print_header(format, true);
    }
}

template <class T> void read_image_pixel( void *imageData, image_descriptor *imageInfo,
                                         int x, int y, int z, T *outData )
{
  read_image_pixel<T>( imageData, imageInfo, x, y, z, outData, 0);
}

// Stupid template rules
bool get_integer_coords( float x, float y, float z,
                        size_t width, size_t height, size_t depth,
                        image_sampler_data *imageSampler, image_descriptor *imageInfo,
                        int &outX, int &outY, int &outZ );
bool get_integer_coords_offset( float x, float y, float z,
                               float xAddressOffset, float yAddressOffset, float zAddressOffset,
                               size_t width, size_t height, size_t depth,
                               image_sampler_data *imageSampler, image_descriptor *imageInfo,
                               int &outX, int &outY, int &outZ );


template <class T> void sample_image_pixel_offset( void *imageData, image_descriptor *imageInfo,
                                                  float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset,
                                                  image_sampler_data *imageSampler, T *outData, int lod )
{
    int iX = 0, iY = 0, iZ = 0;

    float max_w = imageInfo->width;
    float max_h;
    float max_d;

    switch (imageInfo->type) {
        case CL_MEM_OBJECT_IMAGE1D_ARRAY:
            max_h = imageInfo->arraySize;
            max_d = 0;
            break;
        case CL_MEM_OBJECT_IMAGE2D_ARRAY:
            max_h = imageInfo->height;
            max_d = imageInfo->arraySize;
            break;
        default:
            max_h = imageInfo->height;
            max_d = imageInfo->depth;
            break;
    }

    if( /*gTestMipmaps*/ imageInfo->num_mip_levels > 1 )
    {
        switch (imageInfo->type) {
            case CL_MEM_OBJECT_IMAGE3D:
                max_d = (float)((imageInfo->depth >> lod) ? (imageInfo->depth >> lod) : 1);
            case CL_MEM_OBJECT_IMAGE2D:
            case CL_MEM_OBJECT_IMAGE2D_ARRAY:
                max_h = (float)((imageInfo->height >> lod) ? (imageInfo->height >> lod) : 1);
                break;
            default:
                ;

        }
        max_w = (float)((imageInfo->width >> lod) ? (imageInfo->width >> lod) : 1);
    }
    get_integer_coords_offset( x, y, z, xAddressOffset, yAddressOffset, zAddressOffset, max_w, max_h, max_d, imageSampler, imageInfo, iX, iY, iZ );

    read_image_pixel<T>( imageData, imageInfo, iX, iY, iZ, outData, lod );
}

template <class T> void sample_image_pixel_offset( void *imageData, image_descriptor *imageInfo,
                                                  float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset,
                                                  image_sampler_data *imageSampler, T *outData)
{
  sample_image_pixel_offset<T>( imageData, imageInfo, x, y, z, xAddressOffset, yAddressOffset, zAddressOffset,
    imageSampler,  outData, 0);
}

template <class T> void sample_image_pixel( void *imageData, image_descriptor *imageInfo,
                                           float x, float y, float z, image_sampler_data *imageSampler, T *outData )
{
    return sample_image_pixel_offset<T>(imageData, imageInfo, x, y, z, 0.0f, 0.0f, 0.0f, imageSampler, outData);
}

FloatPixel sample_image_pixel_float( void *imageData, image_descriptor *imageInfo,
                                    float x, float y, float z, image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms );

FloatPixel sample_image_pixel_float( void *imageData, image_descriptor *imageInfo,
                                    float x, float y, float z, image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms, int lod );

FloatPixel sample_image_pixel_float_offset( void *imageData, image_descriptor *imageInfo,
                                           float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset,
                                           image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms );
FloatPixel sample_image_pixel_float_offset( void *imageData, image_descriptor *imageInfo,
                                           float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset,
                                           image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms, int lod );


extern void pack_image_pixel( unsigned int *srcVector, const cl_image_format *imageFormat, void *outData );
extern void pack_image_pixel( int *srcVector, const cl_image_format *imageFormat, void *outData );
extern void pack_image_pixel( float *srcVector, const cl_image_format *imageFormat, void *outData );
extern void pack_image_pixel_error( const float *srcVector, const cl_image_format *imageFormat, const void *results,  float *errors );

extern char *create_random_image_data( ExplicitType dataType, image_descriptor *imageInfo, BufferOwningPtr<char> &P, MTdata d, bool image2DFromBuffer = false );

// deprecated
//extern bool clamp_image_coord( image_sampler_data *imageSampler, float value, size_t max, int &outValue );

extern void get_sampler_kernel_code( image_sampler_data *imageSampler, char *outLine );
extern float get_max_absolute_error( cl_image_format *format, image_sampler_data *sampler);
extern float get_max_relative_error( cl_image_format *format, image_sampler_data *sampler, int is3D, int isLinearFilter );


#define errMax( _x , _y )       ( (_x) != (_x) ? (_x) : (_x) > (_y) ? (_x) : (_y) )

static inline cl_uint abs_diff_uint( cl_uint x, cl_uint y )
{
    return y > x ? y - x : x - y;
}

static inline cl_uint abs_diff_int( cl_int x, cl_int y )
{
    return (cl_uint) (y > x ? y - x : x - y);
}

static inline cl_float relative_error( float test, float expected )
{
    // 0-0/0 is 0 in this case, not NaN
    if( test == 0.0f && expected == 0.0f )
        return 0.0f;

    return (test - expected) / expected;
}

extern float random_float(float low, float high);

class CoordWalker
{
public:
    CoordWalker( void * coords, bool useFloats, size_t vecSize );
    ~CoordWalker();

    cl_float    Get( size_t idx, size_t el );

protected:
    cl_float * mFloatCoords;
    cl_int * mIntCoords;
    size_t    mVecSize;
};

extern int  DetectFloatToHalfRoundingMode( cl_command_queue );  // Returns CL_SUCCESS on success

int inline is_half_nan( cl_ushort half ){ return (half & 0x7fff) > 0x7c00; }

cl_ushort convert_float_to_half( cl_float f );
cl_float  convert_half_to_float( cl_ushort h );

extern double sRGBmap(float fc);

#endif // _imageHelpers_h
