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

#include "checker.h"

template < class T> class cImage_check_mem_host_read_only : public cBuffer_checker<T>
{
public:
  cImage_check_mem_host_read_only(cl_device_id deviceID, cl_context context, cl_command_queue queue)
  : cBuffer_checker <T> (deviceID, context, queue)
  {
    m_cl_image_format.image_channel_order = CL_RGBA;
    m_cl_image_format.image_channel_data_type = CL_UNSIGNED_INT8;

    m_cl_Image_desc.image_type = CL_MEM_OBJECT_IMAGE1D;
    m_cl_Image_desc.image_width = 0;
    m_cl_Image_desc.image_height = 0;
    m_cl_Image_desc.image_depth = 0;
    m_cl_Image_desc.image_array_size = 0;
    m_cl_Image_desc.image_row_pitch = 0;
    m_cl_Image_desc.image_slice_pitch = 0;
    m_cl_Image_desc.num_mip_levels = 0;
    m_cl_Image_desc.num_samples = 0;
    m_cl_Image_desc.mem_object = NULL;

    m_Image = NULL;
  };

  ~cImage_check_mem_host_read_only()
  {
  };

  cl_int get_image_elements();

  cl_image_format m_cl_image_format;
  cl_image_desc m_cl_Image_desc;
  clMemWrapper m_Image;

  virtual cl_int SetupImage();
  virtual cl_int SetupBuffer();
  virtual cl_int verify_RW_Image();

  virtual cl_int verify_RW_Image_Mapping();
  virtual cl_int verify_data(T *pdtaIn);
  virtual cl_int verify_data_with_offset(T *pdtaIn, size_t *offset);

  cl_int get_image_content_size();
  cl_int get_image_data_size();

  virtual cl_int verify_RW_Buffer();
  virtual cl_int verify_RW_Buffer_rect();
  virtual cl_int verify_RW_Buffer_mapping();
  cl_int verify_mapping_ptr(T *ptr);
};

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_mapping_ptr( T* dataPtr)
{
  int offset_pixel = (int)(this->buffer_origin[0] + this->buffer_origin[1] *
                           this->buffer_row_pitch_bytes/ sizeof(T) + this->buffer_origin[2] *
                           this->buffer_slice_pitch_bytes/sizeof(T));

  dataPtr = dataPtr - offset_pixel;

  cl_int err = CL_SUCCESS;

  if (this->buffer_mem_flag & CL_MEM_USE_HOST_PTR)
  {
    if (this->pHost_ptr != this->host_m_1.pData)
    {
      log_error("Host memory pointer difference found\n");
      return FAILURE;
    }

    if(dataPtr != this->host_m_1.pData)
    {
      log_error("Mapped host pointer difference found\n");
      return FAILURE;
    }
  }

  return err;
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_RW_Buffer() { return CL_SUCCESS; };

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_RW_Buffer_rect()  { return CL_SUCCESS; };

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_RW_Buffer_mapping() { return CL_SUCCESS; };

template < class T >
cl_int cImage_check_mem_host_read_only< T >::SetupBuffer()
{
  return cBuffer_checker< T >::SetupBuffer();
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::get_image_content_size()
{
  return ((cl_int)(m_cl_Image_desc.image_width*m_cl_Image_desc.image_height *
                   m_cl_Image_desc.image_depth * m_cl_Image_desc.image_array_size));
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::get_image_data_size()
{
  size_t slice_pitch = m_cl_Image_desc.image_slice_pitch ? m_cl_Image_desc.image_slice_pitch :
    (m_cl_Image_desc.image_height *  m_cl_Image_desc.image_width);
  return (slice_pitch * m_cl_Image_desc.image_depth * m_cl_Image_desc.image_array_size);
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::get_image_elements()
{
  return ((cl_int)(m_cl_Image_desc.image_width*m_cl_Image_desc.image_height *
                   m_cl_Image_desc.image_depth * m_cl_Image_desc.image_array_size));
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::SetupImage()
{
  int all = (int)(m_cl_Image_desc.image_width * m_cl_Image_desc.image_height *
                  m_cl_Image_desc.image_depth * m_cl_Image_desc.image_array_size);

  T v = TEST_VALUE;
  this->host_m_1.Init(all, v);

  cl_int err = CL_SUCCESS;
  this-> m_Image = clCreateImage(this->m_context, this->buffer_mem_flag,
                                 &( this-> m_cl_image_format), &(this-> m_cl_Image_desc),
                                 this->host_m_1.pData, &err);
  test_error(err , "clCreateImage error");

  this-> pHost_ptr = (void *) (this->host_m_1.pData);

  return err;
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_data(T *pDataIN)
{
  cl_int err = CL_SUCCESS;
  if (!this->host_m_1.Equal_rect_from_orig(pDataIN, this->buffer_origin,
                                           this->region, this->host_row_pitch,
                                           this->host_slice_pitch)) {
    log_error("Buffer data difference found\n");
    return FAILURE;
  }

  return err;
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_data_with_offset(T *pDataIN,
                                                                     size_t *offset)
{
  cl_int err = CL_SUCCESS;
  if (!this->host_m_2.Equal_rect_from_orig(pDataIN, offset, this->region,
                                           this->host_row_pitch,
                                           this->host_slice_pitch)) {
    log_error("Buffer data difference found\n");
    return FAILURE;
  }

  return err;
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_RW_Image()
{
  this->Init_rect();

  int imge_content_size = this->get_image_content_size();
  T v = 0;
  this->host_m_2.Init( imge_content_size, v);

  cl_event event;
  cl_int err = CL_SUCCESS;
  err = clEnqueueReadImage(this->m_queue, this->m_Image, this->m_blocking,
                           this->buffer_origin, this->region,
                           this-> buffer_row_pitch_bytes, this->buffer_slice_pitch_bytes,
                           this->host_m_2.pData, 0, NULL, &event);

  test_error(err, "clEnqueueReadImage error");

  if ( !this->m_blocking) {
    err = clWaitForEvents(1, &event);
    test_error(err, "clWaitForEvents error");
  }

  err = this->verify_data(this->host_m_2.pData);
  test_error(err, "verify_data error");

  err = clEnqueueWriteImage(this->m_queue, this->m_Image, this->m_blocking,
                            this->buffer_origin, this->region,
                            this->buffer_row_pitch_bytes, this->buffer_slice_pitch_bytes,
                            this->host_m_2.pData, 0, NULL, &event);

  if (err == CL_SUCCESS) {
    log_error("Calling clEnqueueWriteImage on a memory object created with the CL_MEM_HOST_READ_ONLY flag should not return CL_SUCCESS\n");
    err = FAILURE;
    return FAILURE;

  } else {
    log_info("Test succeeded\n\n");
    err = CL_SUCCESS;
  }

  return err;
}

template < class T >
cl_int cImage_check_mem_host_read_only< T >::verify_RW_Image_Mapping()
{
  cl_event event;
  cl_int err = CL_SUCCESS;

  T * dataPtr = (T*) clEnqueueMapImage(this->m_queue, this->m_Image, this->m_blocking,
                                       CL_MAP_READ,
                                       this->buffer_origin, this->region,
                                       &(this-> buffer_row_pitch_bytes),
                                       &(this->buffer_slice_pitch_bytes),
                                       0, NULL, &event, &err);

  if (!this->m_blocking) {
    err = clWaitForEvents(1, &event);
    test_error(err, "clWaitForEvents error");
  }

  err= this->verify_mapping_ptr(dataPtr);
  test_error(err, "clEnqueueMapImage error");

  err = this->verify_data(dataPtr);
  test_error(err, "verify_data error");

  err= clEnqueueUnmapMemObject (this->m_queue, this->m_Image, dataPtr, 0, NULL, &event);
  test_error(err, "clEnqueueUnmapMemObject error");

  err = clWaitForEvents(1, &event);
  test_error(err, "clWaitForEvents error");

  dataPtr = (T*) clEnqueueMapImage(this->m_queue, this->m_Image, this->m_blocking,
                                   CL_MAP_WRITE,
                                   this->buffer_origin,
                                   this->region,
                                   &(this-> buffer_row_pitch_bytes),
                                   &(this->buffer_slice_pitch_bytes),
                                   0, NULL, &event, &err);

  if (err == CL_SUCCESS) {
    log_error("Calling clEnqueueMapImage (CL_MAP_WRITE) on a memory object created with the CL_MEM_HOST_READ_ONLY flag should not return CL_SUCCESS\n");
    err = FAILURE;
    return FAILURE;

  } else {
    log_info("Test succeeded\n\n");
    err = CL_SUCCESS;
  }

  return err;
}

#endif
