//
// 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_NO_ACCESS_h
#define test_conformance_checker_Image_MEM_HOST_NO_ACCESS_h

#include "checker_image_mem_host_write_only.hpp"

template <class T>
class cImage_check_mem_host_no_access
    : public cImage_check_mem_host_write_only<T> {
public:
    cImage_check_mem_host_no_access(cl_device_id deviceID, cl_context context,
                                    cl_command_queue queue)
        : cImage_check_mem_host_write_only<T>(deviceID, context, queue)
    {}

    ~cImage_check_mem_host_no_access(){};

    cl_int verify_RW_Image();
    cl_int verify_RW_Image_Mapping();
};

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

    cl_event event;
    size_t img_orig[3] = { 0, 0, 0 };
    size_t img_region[3] = { 0, 0, 0 };
    img_region[0] = this->m_cl_Image_desc.image_width;
    img_region[1] = this->m_cl_Image_desc.image_height;
    img_region[2] = this->m_cl_Image_desc.image_depth;

    int color[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
    cl_int err = CL_SUCCESS;
    err = clEnqueueFillImage(this->m_queue, this->m_Image, &color, img_orig,
                             img_region, 0, NULL, &event);
    test_error(err, "clEnqueueFillImage error");

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

    err = clReleaseEvent(event);
    test_error(err, "clReleaseEvent error");

    this->update_host_mem_2();

    int total = (int)(this->region[0] * this->region[1] * this->region[2]);

    T v = 0xFFFFFFFF;
    int tot = (int)(this->host_m_2.Count(v));
    if (tot != total)
    {
        log_error("Buffer data content difference found\n");
        return FAILURE;
    }

    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_1.pData, 0, NULL, &event);

    if (err == CL_SUCCESS)
    {
        log_error(
            "Calling clEnqueueWriteImage on a memory object created with the "
            "CL_MEM_HOST_NO_ACCESS flag should not return CL_SUCCESS\n");
        err = FAILURE;
        return err;
    }
    else
    {
        log_info("Test succeeded\n\n");
        err = CL_SUCCESS;
    }

    v = 0;
    this->host_m_2.Set_to(v);
    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);

    if (err == CL_SUCCESS)
    {
        log_error(
            "Calling clEnqueueReadImage on a memory object created with the "
            "CL_MEM_HOST_NO_ACCESS flag should not return CL_SUCCESS\n");
        err = FAILURE;
        return err;
    }
    else
    {
        log_info("Test succeeded\n\n");
        err = CL_SUCCESS;
    }

    return err;
}

template <class T>
cl_int cImage_check_mem_host_no_access<T>::verify_RW_Image_Mapping()
{
    this->Init_rect();

    cl_event event;
    cl_int err = CL_SUCCESS;

    T* 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_NO_ACCESS flag should not "
                  "return CL_SUCCESS\n");
        err = FAILURE;
        return err;
    }
    else if (dataPtr != nullptr)
    {
        log_error("Calling clEnqueueMapImage (CL_MAP_WRITE) on a memory object "
                  "created with the CL_MEM_HOST_NO_ACCESS flag should fail "
                  "and return NULL\n");
        err = FAILURE;
        return err;
    }
    else
    {
        log_info("Test succeeded\n\n");
        err = CL_SUCCESS;
    }

    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 (err == CL_SUCCESS)
    {
        log_error("Calling clEnqueueMapImage (CL_MAP_READ) on a memory object "
                  "created with the CL_MEM_HOST_NO_ACCESS flag should not "
                  "return CL_SUCCESS\n");
        err = FAILURE;
        return err;
    }
    else if (dataPtr != nullptr)
    {
        log_error("Calling clEnqueueMapImage (CL_MAP_READ) on a memory object "
                  "created with the CL_MEM_HOST_NO_ACCESS flag should fail "
                  "and return NULL\n");
        err = FAILURE;
        return err;
    }
    else
    {
        log_info("Test succeeded\n\n");
        err = CL_SUCCESS;
    }

    return err;
}

#endif
