//
// 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.
//
#include "../../test_common/harness/compat.h"

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

#include "procs.h"

#include "checker_mem_host_read_only.hpp"
#include "checker_mem_host_write_only.hpp"
#include "checker_mem_host_no_access.hpp"

static int test_mem_host_read_only_buffer_RW(cl_device_id deviceID, cl_context context,
                                             cl_command_queue queue, cl_bool blocking,
                                             cl_mem_flags buffer_mem_flag,
                                             cl_mem_flags parent_buffer_flag,
                                             enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);
  cBuffer_check_mem_host_read_only< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);
  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err = checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err = checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err= checker.verify_RW_Buffer();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

static int test_mem_host_read_only_buffer_RW_Rect(cl_device_id deviceID, cl_context context,
                                                  cl_command_queue queue, cl_bool blocking,
                                                  cl_mem_flags buffer_mem_flag,
                                                  cl_mem_flags parent_buffer_flag,
                                                  enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);

  cBuffer_check_mem_host_read_only< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);
  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err= checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err= checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err = checker.verify_RW_Buffer_rect();
  test_error(err,  __FUNCTION__);
  clFinish(queue);

  return err;
}

static int test_mem_host_read_only_buffer_RW_Mapping(cl_device_id deviceID, cl_context context,
                                                     cl_command_queue queue, cl_bool blocking,
                                                     cl_mem_flags buffer_mem_flag,
                                                     cl_mem_flags parent_buffer_flag,
                                                     enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);

  cBuffer_check_mem_host_read_only< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);
  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err= checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err= checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err = checker.verify_RW_Buffer_mapping();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

int test_mem_host_read_only_buffer(cl_device_id deviceID, cl_context context,
                                   cl_command_queue queue, int num_elements)
{
  cl_mem_flags buffer_mem_flags[2] = {CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_READ_ONLY,
    CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR | CL_MEM_HOST_READ_ONLY};

  cl_int err = CL_SUCCESS;

  cl_bool blocking[2] = {CL_TRUE, CL_FALSE};
  for (int k=0; k<2; k++)
    for (int i=0; i< 2; i++)
    {
      err = test_mem_host_read_only_buffer_RW(deviceID, context, queue, blocking[i],
                                              buffer_mem_flags[k], 0, _BUFFER);
      test_error(err, __FUNCTION__);

      err = test_mem_host_read_only_buffer_RW_Rect(deviceID, context, queue, blocking[i],
                                                   buffer_mem_flags[k],0, _BUFFER);
      test_error(err, __FUNCTION__);

      err = test_mem_host_read_only_buffer_RW_Mapping(deviceID, context, queue, blocking[i],
                                                      buffer_mem_flags[k],0, _BUFFER);
      test_error(err, __FUNCTION__);
    }

  return err;
}

int test_mem_host_read_only_subbuffer(cl_device_id deviceID, cl_context context,
                                      cl_command_queue queue, int num_elements)
{
  cl_mem_flags parent_buffer_mem_flags[1] = {CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_READ_ONLY};

  cl_mem_flags buffer_mem_flags[4] = {0, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR,
    CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR,
    CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR};

  cl_int err = CL_SUCCESS;

  cl_bool blocking[2] = {CL_TRUE, CL_FALSE};

  for (int p=0; p<1; p++) {
    for (int k=0; k<4; k++)
      for (int i=0; i<2; i++)
      {
        err = test_mem_host_read_only_buffer_RW(deviceID, context, queue, blocking[i],
                                                buffer_mem_flags[k], parent_buffer_mem_flags[p], _Sub_BUFFER);
        test_error(err, __FUNCTION__);

        err = test_mem_host_read_only_buffer_RW_Rect(deviceID, context, queue, blocking[i],
                                                     buffer_mem_flags[k], parent_buffer_mem_flags[p], _Sub_BUFFER);
        test_error(err, __FUNCTION__);

        err = test_mem_host_read_only_buffer_RW_Mapping(deviceID, context, queue, blocking[i],
                                                        buffer_mem_flags[k], parent_buffer_mem_flags[p], _Sub_BUFFER);
        test_error(err, __FUNCTION__);
      }
  }

  return err;
}

//=============================== Write only

static cl_int test_mem_host_write_only_buffer_RW(cl_device_id deviceID, cl_context context,
                                                 cl_command_queue queue, cl_bool blocking,
                                                 cl_mem_flags buffer_mem_flag,
                                                 cl_mem_flags parent_buffer_flag,
                                                 enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);

  cBuffer_check_mem_host_write_only< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);

  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err = checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err = checker.SetupASSubBuffer( parent_buffer_flag );
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err= checker.verify_RW_Buffer();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

static cl_int test_mem_host_write_only_buffer_RW_Rect(cl_device_id deviceID, cl_context context,
                                                      cl_command_queue queue, cl_bool blocking,
                                                      cl_mem_flags buffer_mem_flag,
                                                      cl_mem_flags parent_buffer_flag,
                                                      enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);

  cBuffer_check_mem_host_write_only< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);
  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err= checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err= checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err= checker.verify_RW_Buffer_rect();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

static cl_int test_mem_host_write_only_buffer_RW_Mapping(cl_device_id deviceID, cl_context context,
                                                         cl_command_queue queue, cl_bool blocking,
                                                         cl_mem_flags buffer_mem_flag,
                                                         cl_mem_flags parent_buffer_flag,
                                                         enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);

  cBuffer_check_mem_host_write_only< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);
  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err= checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err= checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err= checker.verify_RW_Buffer_mapping();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

int test_mem_host_write_only_buffer(cl_device_id deviceID, cl_context context,
                                    cl_command_queue queue, int num_elements)
{
  cl_mem_flags buffer_mem_flags[2] = {CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_WRITE_ONLY,
    CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR | CL_MEM_HOST_WRITE_ONLY};

  cl_int err = CL_SUCCESS;

  cl_bool blocking[2] = {CL_TRUE, CL_FALSE};
  for (int k=0; k<2; k++)
    for (int i=0; i<2; i++)
    {
      err = test_mem_host_write_only_buffer_RW(deviceID, context, queue, blocking[i],
                                               buffer_mem_flags[k], 0, _BUFFER);
      test_error(err, __FUNCTION__);

      err = test_mem_host_write_only_buffer_RW_Rect(deviceID, context, queue, blocking[i],
                                                    buffer_mem_flags[k], 0, _BUFFER);
      test_error(err, __FUNCTION__);

      err = test_mem_host_write_only_buffer_RW_Mapping(deviceID, context, queue, blocking[i],
                                                       buffer_mem_flags[k], 0, _BUFFER);
      test_error(err, __FUNCTION__);
    }

  return err;
}

int test_mem_host_write_only_subbuffer(cl_device_id deviceID, cl_context context,
                                       cl_command_queue queue, int num_elements)
{
  cl_mem_flags parent_buffer_mem_flags[1] = {CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_WRITE_ONLY};

  cl_mem_flags buffer_mem_flags[4] = {0, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR,
    CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR,
    CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR};

  cl_int err = CL_SUCCESS;

  cl_bool blocking[2] = {CL_TRUE, CL_FALSE};

  for (int p=0; p<1; p++) {
    for (int m=0; m<4; m++) {
      for (int i=0; i< 2; i++)
      {
        err = test_mem_host_write_only_buffer_RW(deviceID, context, queue, blocking[i],
                                                 buffer_mem_flags[m], parent_buffer_mem_flags[p], _Sub_BUFFER);
        test_error(err, __FUNCTION__);

        err = test_mem_host_write_only_buffer_RW_Rect(deviceID, context, queue, blocking[i],
                                                      buffer_mem_flags[m], parent_buffer_mem_flags[p], _Sub_BUFFER);
        test_error(err, __FUNCTION__);

        err = test_mem_host_write_only_buffer_RW_Mapping(deviceID, context, queue, blocking[i],
                                                         buffer_mem_flags[m] , parent_buffer_mem_flags[p], _Sub_BUFFER);
        test_error(err, __FUNCTION__);
      }
    }
  }

  return err;
}

//=====================  NO ACCESS

static cl_int test_mem_host_no_access_buffer_RW(cl_device_id deviceID, cl_context context,
                                                cl_command_queue queue, cl_bool blocking,
                                                cl_mem_flags buffer_mem_flag,
                                                cl_mem_flags parent_buffer_flag,
                                                enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);

  cBuffer_check_mem_host_no_access< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);
  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;

  cl_int err = CL_SUCCESS;
  switch (buffer_type) {
    case _BUFFER:
      err= checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err= checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err= checker.verify_RW_Buffer_mapping();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

static cl_int test_mem_host_no_access_buffer_RW_Rect(cl_device_id deviceID, cl_context context,
                                                     cl_command_queue queue, cl_bool blocking,
                                                     cl_mem_flags buffer_mem_flag,
                                                     cl_mem_flags parent_buffer_flag,
                                                     enum BUFFER_TYPE buffer_type)
{
  log_info( "%s\n", __FUNCTION__);

  cBuffer_check_mem_host_no_access< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);
  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err= checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err= checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err= checker.verify_RW_Buffer_mapping();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

static cl_int test_mem_host_no_access_buffer_RW_Mapping(cl_device_id deviceID, cl_context context,
                                                        cl_command_queue queue, cl_bool blocking,
                                                        cl_mem_flags buffer_mem_flag,
                                                        cl_mem_flags parent_buffer_flag,
                                                        enum BUFFER_TYPE buffer_type)
{
  log_info("%s\n", __FUNCTION__);

  cBuffer_check_mem_host_no_access< TEST_ELEMENT_TYPE > checker(deviceID, context, queue);

  checker.m_blocking = blocking;
  checker.buffer_mem_flag = buffer_mem_flag;
  cl_int err;
  switch (buffer_type) {
    case _BUFFER:
      err= checker.SetupBuffer();
      break;
    case _Sub_BUFFER:
      err= checker.SetupASSubBuffer(parent_buffer_flag);
      break;
  }

  test_error(err, __FUNCTION__);
  checker.Setup_Test_Environment();
  err= checker.verify_RW_Buffer_mapping();
  test_error(err, __FUNCTION__);
  clFinish(queue);

  return err;
}

int test_mem_host_no_access_buffer(cl_device_id deviceID, cl_context context,
                                   cl_command_queue queue, int num_elements)
{
  cl_mem_flags buffer_mem_flag[2] = {CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_NO_ACCESS,
    CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR | CL_MEM_HOST_NO_ACCESS};

  cl_int err = CL_SUCCESS;

  cl_bool blocking[2] = {CL_TRUE, CL_FALSE};
  for (int k=0; k<2; k++)
    for (int i=0; i<2; i++) {
      err = test_mem_host_no_access_buffer_RW(deviceID, context, queue, blocking[i],
                                              buffer_mem_flag[k], 0, _BUFFER);
      test_error(err, __FUNCTION__);

      err = test_mem_host_no_access_buffer_RW_Rect(deviceID, context, queue, blocking[i],
                                                   buffer_mem_flag[k], 0, _BUFFER);
      test_error(err, __FUNCTION__);

      err = test_mem_host_no_access_buffer_RW_Mapping(deviceID, context, queue, blocking[i],
                                                      buffer_mem_flag[k], 0, _BUFFER);
      test_error(err, __FUNCTION__);
    }

  return err;
}

int test_mem_host_no_access_subbuffer(cl_device_id deviceID, cl_context context,
                                      cl_command_queue queue, int num_elements)
{
  cl_mem_flags parent_buffer_mem_flags[3] = { CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_NO_ACCESS,
    CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_NO_ACCESS,
    CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR | CL_MEM_HOST_NO_ACCESS};

  cl_mem_flags buffer_mem_flags[4] = {0, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR,
    CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR,
    CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR};

  cl_int err = CL_SUCCESS;

  cl_bool blocking[2] = {CL_TRUE, CL_FALSE};
  for (int p=0; p<3; p++) {
    for (int k=0; k<4; k++) {
      for (int i=0; i<2; i++) {
        err += test_mem_host_no_access_buffer_RW(deviceID, context, queue, blocking[i],
                                                buffer_mem_flags[k], parent_buffer_mem_flags[p], _Sub_BUFFER);

        err += test_mem_host_no_access_buffer_RW_Rect(deviceID, context, queue, blocking[i],
                                                     buffer_mem_flags[k], parent_buffer_mem_flags[p], _Sub_BUFFER);

        err += test_mem_host_no_access_buffer_RW_Mapping( deviceID, context, queue, blocking[i],
                                                        buffer_mem_flags[k], parent_buffer_mem_flags[p], _Sub_BUFFER);
      }
    }
  }

  return err;
}
