//
// 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 _testHarness_h
#define _testHarness_h

#include "threadTesting.h"
#include "clImageHelper.h"

#ifdef __cplusplus
extern "C" {
#endif

#define ADD_TEST(fn) {test_##fn, #fn}
#define NOT_IMPLEMENTED_TEST(fn) {NULL, #fn}

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

typedef struct test_definition
{
    basefn func;
    const char* name;
} test_definition;

typedef enum test_status
{
    TEST_PASS = 0,
    TEST_FAIL = 1,
    TEST_SKIP = 2,
} test_status;

extern cl_uint gReSeed;
extern cl_uint gRandomSeed;

// Supply a list of functions to test here. This will allocate a CL device, create a context, all that
// setup work, and then call each function in turn as dictatated by the passed arguments.
extern int runTestHarness(int argc, const char *argv[], int testNum, test_definition testList[],
                           int imageSupportRequired, int forceNoContextCreation, cl_command_queue_properties queueProps );

// Device checking function. See runTestHarnessWithCheck. If this function returns anything other than TEST_PASS, the harness exits.
typedef test_status (*DeviceCheckFn)( cl_device_id device );

// Same as runTestHarness, but also supplies a function that checks the created device for required functionality.
extern int runTestHarnessWithCheck( int argc, const char *argv[], int testNum, test_definition testList[],
                                    int imageSupportRequired, int forceNoContextCreation, cl_command_queue_properties queueProps,
                                    DeviceCheckFn deviceCheckFn );

// The command line parser used by runTestHarness to break up parameters into calls to callTestFunctions
extern int parseAndCallCommandLineTests( int argc, const char *argv[], cl_device_id device, int testNum,
                                         test_definition testList[], int forceNoContextCreation,
                                         cl_command_queue_properties queueProps, int num_elements );

// Call this function if you need to do all the setup work yourself, and just need the function list called/
// managed.
//    testList is the data structure that contains test functions and its names
//    selectedTestList is an array of integers (treated as bools) which tell which function is to be called,
//       each element at index i, corresponds to the element in testList at index i
//    resultTestList is an array of integers which contains the result of each selected test
//    testNum is the number of tests in testList, selectedTestList and resultTestList
//    contextProps are used to create a testing context for each test
//    deviceToUse and numElementsToUse are all just passed to each test function
extern int callTestFunctions( test_definition testList[], unsigned char selectedTestList[], int resultTestList[],
                              int testNum, cl_device_id deviceToUse, int forceNoContextCreation, int numElementsToUse,
                              cl_command_queue_properties queueProps );

// This function is called by callTestFunctions, once per function, to do setup, call, logging and cleanup
extern int callSingleTestFunction( test_definition test, cl_device_id deviceToUse, int forceNoContextCreation,
                                   int numElementsToUse, cl_command_queue_properties queueProps );

///// Miscellaneous steps

// Given a pre-existing device type choice, check the environment for an override, then print what
// choice was made and how (and return the overridden choice, if there is one)
extern void checkDeviceTypeOverride( cl_device_type *inOutType );

// standard callback function for context pfn_notify
extern void CL_CALLBACK notify_callback(const char *errinfo, const void *private_info, size_t cb, void *user_data);

extern cl_device_type GetDeviceType( cl_device_id );

// Given a device (most likely passed in by the harness, but not required), will attempt to find
// a DIFFERENT device and return it. Useful for finding another device to run multi-device tests against.
// Note that returning NULL means an error was hit, but if no error was hit and the device passed in
// is the only device available, the SAME device is returned, so check!
extern cl_device_id GetOpposingDevice( cl_device_id device );


extern int      gFlushDenormsToZero;    // This is set to 1 if the device does not support denorms (CL_FP_DENORM)
extern int      gInfNanSupport;         // This is set to 1 if the device supports infinities and NaNs
extern int        gIsEmbedded;            // This is set to 1 if the device is an embedded device
extern int        gHasLong;               // This is set to 1 if the device suppots long and ulong types in OpenCL C.
extern int      gIsOpenCL_C_1_0_Device; // This is set to 1 if the device supports only OpenCL C 1.0.

#if ! defined( __APPLE__ )
    void     memset_pattern4(void *, const void *, size_t);
#endif

#ifdef __cplusplus
}
#endif

#endif // _testHarness_h


