/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * 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 <gtest/gtest.h>
#include <iservice_registry.h>
#include <native_image.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "graphic_common_c.h"
#include "surface_type.h"
#include "window.h"
#include "GLES/gl.h"
#include "buffer_log.h"

using namespace testing;
using namespace testing::ext;
using namespace std;

namespace OHOS::Rosen {
using GetPlatformDisplayExt = PFNEGLGETPLATFORMDISPLAYEXTPROC;
constexpr const char* EGL_EXT_PLATFORM_WAYLAND = "EGL_EXT_platform_wayland";
constexpr const char* EGL_KHR_PLATFORM_WAYLAND = "EGL_KHR_platform_wayland";
constexpr int32_t EGL_CONTEXT_CLIENT_VERSION_NUM = 2;
constexpr char CHARACTER_WHITESPACE = ' ';
constexpr const char* CHARACTER_STRING_WHITESPACE = " ";
constexpr const char* EGL_GET_PLATFORM_DISPLAY_EXT = "eglGetPlatformDisplayEXT";

struct TEST_IMAGE {
    int a;
    bool b;
};

static bool CheckEglExtension(const char* extensions, const char* extension)
{
    size_t extlen = strlen(extension);
    const char* end = extensions + strlen(extensions);

    while (extensions < end) {
        size_t n = 0;
        /* Skip whitespaces, if any */
        if (*extensions == CHARACTER_WHITESPACE) {
            extensions++;
            continue;
        }
        n = strcspn(extensions, CHARACTER_STRING_WHITESPACE);
        /* Compare strings */
        if (n == extlen && strncmp(extension, extensions, n) == 0) {
            return true; /* Found */
        }
        extensions += n;
    }
    /* Not found */
    return false;
}

static EGLDisplay GetPlatformEglDisplay(EGLenum platform, void* native_display, const EGLint* attrib_list)
{
    static GetPlatformDisplayExt eglGetPlatformDisplayExt = NULL;

    if (!eglGetPlatformDisplayExt) {
        const char* extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
        if (extensions &&
            (CheckEglExtension(extensions, EGL_EXT_PLATFORM_WAYLAND) ||
                CheckEglExtension(extensions, EGL_KHR_PLATFORM_WAYLAND))) {
            eglGetPlatformDisplayExt = (GetPlatformDisplayExt)eglGetProcAddress(EGL_GET_PLATFORM_DISPLAY_EXT);
        }
    }

    if (eglGetPlatformDisplayExt) {
        return eglGetPlatformDisplayExt(platform, native_display, attrib_list);
    }

    return eglGetDisplay((EGLNativeDisplayType)native_display);
}

class NativeImageTest : public testing::Test {
public:
    static void SetUpTestCase();
    static void TearDownTestCase();

    static void InitEglContext();
    static void Deinit();

    static inline OH_NativeImage* image = nullptr;
    static inline OHNativeWindow* nativeWindow = nullptr;
    static inline GLuint textureId = 0;
    static inline GLuint textureId2 = 0;
    static inline EGLDisplay eglDisplay_ = EGL_NO_DISPLAY;
    static inline EGLContext eglContext_ = EGL_NO_CONTEXT;
    static inline EGLConfig config_;;
};

void NativeImageTest::SetUpTestCase()
{
    image = nullptr;
    nativeWindow = nullptr;
    glGenTextures(1, &textureId);
    glGenTextures(1, &textureId2);
}

void NativeImageTest::TearDownTestCase()
{
    image = nullptr;
    nativeWindow = nullptr;
    Deinit();
}

void NativeImageTest::InitEglContext()
{
    if (eglContext_ != EGL_NO_DISPLAY) {
        return;
    }

    BLOGI("Creating EGLContext!!!");
    eglDisplay_ = GetPlatformEglDisplay(EGL_PLATFORM_OHOS_KHR, EGL_DEFAULT_DISPLAY, NULL);
    if (eglDisplay_ == EGL_NO_DISPLAY) {
        BLOGW("Failed to create EGLDisplay gl errno : %{public}x", eglGetError());
        return;
    }

    EGLint major, minor;
    if (eglInitialize(eglDisplay_, &major, &minor) == EGL_FALSE) {
        BLOGE("Failed to initialize EGLDisplay");
        return;
    }

    if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
        BLOGE("Failed to bind OpenGL ES API");
        return;
    }

    unsigned int ret;
    EGLint count;
    EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_NONE };

    ret = eglChooseConfig(eglDisplay_, config_attribs, &config_, 1, &count);
    if (!(ret && static_cast<unsigned int>(count) >= 1)) {
        BLOGE("Failed to eglChooseConfig");
        return;
    }

    static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, EGL_CONTEXT_CLIENT_VERSION_NUM, EGL_NONE };

    eglContext_ = eglCreateContext(eglDisplay_, config_, EGL_NO_CONTEXT, context_attribs);
    if (eglContext_ == EGL_NO_CONTEXT) {
        BLOGE("Failed to create egl context %{public}x", eglGetError());
        return;
    }

    eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, eglContext_);

    BLOGW("Create EGL context successfully, version %{public}d.%{public}d", major, minor);
}

void NativeImageTest::Deinit()
{
    if (eglDisplay_ == EGL_NO_DISPLAY) {
        return;
    }
    eglDestroyContext(eglDisplay_, eglContext_);
    eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglTerminate(eglDisplay_);
    eglReleaseThread();

    eglDisplay_ = EGL_NO_DISPLAY;
    eglContext_ = EGL_NO_CONTEXT;
}

/*
 * @tc.name: OHNativeImageCreate001
 * @tc.desc: test for call OH_NativeImage_Create and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageCreate001, Function | MediumTest | Level1)
{
    image = OH_NativeImage_Create(textureId, GL_TEXTURE_2D);
    ASSERT_NE(image, nullptr);
}

/*
 * @tc.name: OHNativeImageAcquireNativeWindow001
 * @tc.desc: test for call OH_NativeImage_AcquireNativeWindow by abnormal input and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageAcquireNativeWindow001, Function | MediumTest | Level2)
{
    nativeWindow = OH_NativeImage_AcquireNativeWindow(nullptr);
    ASSERT_EQ(nativeWindow, nullptr);
}

/*
 * @tc.name: OHNativeImageAcquireNativeWindow001
 * @tc.desc: test for call OH_NativeImage_AcquireNativeWindow and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageAcquireNativeWindow002, Function | MediumTest | Level1)
{
    nativeWindow = OH_NativeImage_AcquireNativeWindow(image);
    ASSERT_NE(nativeWindow, nullptr);
}

/*
 * @tc.name: OHNativeImageAttachContext001
 * @tc.desc: test for call OH_NativeImage_AttachContext by abnormal input and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageAttachContext001, Function | MediumTest | Level2)
{
    int32_t ret = OH_NativeImage_AttachContext(nullptr, textureId);
    ASSERT_NE(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageDetachContext001
 * @tc.desc: test for call OHNativeImageDetachContext by abnormal input and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageDetachContext001, Function | MediumTest | Level2)
{
    int32_t ret = OH_NativeImage_DetachContext(nullptr);
    ASSERT_NE(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageDetachContext002
 * @tc.desc: test for call OHNativeImageDetachContext and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageDetachContext002, Function | MediumTest | Level1)
{
    int32_t ret = OH_NativeImage_DetachContext(image);
    ASSERT_EQ(ret, SURFACE_ERROR_INIT);
}

/*
 * @tc.name: OHNativeImageDetachContext003
 * @tc.desc: test for call OHNativeImageDetachContext and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageDetachContext003, Function | MediumTest | Level1)
{
    InitEglContext();
    int32_t ret = OH_NativeImage_DetachContext(image);
    ASSERT_EQ(ret, SURFACE_ERROR_ERROR);
}

/*
 * @tc.name: OHNativeImageDetachContext003
 * @tc.desc: test for call OH_NativeImage_AttachContext and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageAttachContext002, Function | MediumTest | Level1)
{
    int32_t ret = OH_NativeImage_AttachContext(image, textureId);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageUpdateSurfaceImage001
 * @tc.desc: test for  call OH_NativeImage_UpdateSurfaceImage by abnormal input and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageUpdateSurfaceImage001, Function | MediumTest | Level2)
{
    int32_t ret = OH_NativeImage_UpdateSurfaceImage(nullptr);
    ASSERT_NE(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageUpdateSurfaceImage002
 * @tc.desc: test for call OH_NativeImage_UpdateSurfaceImage and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageUpdateSurfaceImage002, Function | MediumTest | Level1)
{
    int32_t ret = OH_NativeImage_UpdateSurfaceImage(image);
    ASSERT_NE(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageUpdateSurfaceImage003
 * @tc.desc: test for call OH_NativeImage_UpdateSurfaceImage.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageUpdateSurfaceImage003, Function | MediumTest | Level1)
{
    int code = SET_USAGE;
    int32_t usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA;
    int32_t ret = NativeWindowHandleOpt(nativeWindow, code, usage);
    if (ret != GSERROR_OK) {
        std::cout << "NativeWindowHandleOpt SET_USAGE faile" << std::endl;
    }
    code = SET_BUFFER_GEOMETRY;
    int32_t width = 0x100;
    int32_t height = 0x100;
    ret = NativeWindowHandleOpt(nativeWindow, code, width, height);
    if (ret != GSERROR_OK) {
        std::cout << "NativeWindowHandleOpt SET_BUFFER_GEOMETRY failed" << std::endl;
    }
    code = SET_STRIDE;
    int32_t stride = 0x8;
    ret = NativeWindowHandleOpt(nativeWindow, code, stride);
    if (ret != GSERROR_OK) {
        std::cout << "NativeWindowHandleOpt SET_STRIDE failed" << std::endl;
    }
    code = SET_FORMAT;
    int32_t format = PIXEL_FMT_RGBA_8888;
    ret = NativeWindowHandleOpt(nativeWindow, code, format);
    if (ret != GSERROR_OK) {
        std::cout << "NativeWindowHandleOpt SET_FORMAT failed" << std::endl;
    }

    NativeWindowBuffer* nativeWindowBuffer = nullptr;
    int fenceFd = -1;
    ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &nativeWindowBuffer, &fenceFd);
    ASSERT_EQ(ret, GSERROR_OK);

    struct Region *region = new Region();
    struct Region::Rect *rect = new Region::Rect();
    rect->x = 0x100;
    rect->y = 0x100;
    rect->w = 0x100;
    rect->h = 0x100;
    region->rects = rect;
    ret = OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, nativeWindowBuffer, fenceFd, *region);
    ASSERT_EQ(ret, GSERROR_OK);
    delete region;

    ret = OH_NativeImage_UpdateSurfaceImage(image);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageGetTimestamp001
 * @tc.desc: test for call OH_NativeImage_GetTimestamp by abnormal input and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageGetTimestamp001, Function | MediumTest | Level2)
{
    int64_t timeStamp = OH_NativeImage_GetTimestamp(nullptr);
    ASSERT_EQ(timeStamp, SURFACE_ERROR_ERROR);
}

/*
 * @tc.name: OHNativeImageGetTimestamp002
 * @tc.desc: test for call OH_NativeImage_GetTimestamp and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageGetTimestamp002, Function | MediumTest | Level1)
{
    int64_t timeStamp = OH_NativeImage_GetTimestamp(image);
    ASSERT_NE(timeStamp, SURFACE_ERROR_ERROR);
}

/*
 * @tc.name: OHNativeImageGetTransformMatrix001
 * @tc.desc: test for call OH_NativeImage_GetTransformMatrix by abnormal input and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageGetTransformMatrix001, Function | MediumTest | Level2)
{
    float matrix[16];
    int32_t ret = OH_NativeImage_GetTransformMatrix(nullptr, matrix);
    ASSERT_NE(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageGetTransformMatrix002
 * @tc.desc: test for call OH_NativeImage_GetTransformMatrix and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageGetTransformMatrix002, Function | MediumTest | Level1)
{
    float matrix[16];
    int32_t ret = OH_NativeImage_GetTransformMatrix(image, matrix);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageGetTransformMatrix003
 * @tc.desc: test for call OH_NativeImage_GetTransformMatrix with another texture and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageAttachContext003, Function | MediumTest | Level1)
{
    int32_t ret = OH_NativeImage_AttachContext(image, textureId2);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageUpdateSurfaceImage004
 * @tc.desc: test for OH_NativeImage_UpdateSurfaceImage after the OPENGL ES texture changed and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageUpdateSurfaceImage004, Function | MediumTest | Level1)
{
    NativeWindowBuffer* nativeWindowBuffer = nullptr;
    int fenceFd = -1;
    int32_t ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &nativeWindowBuffer, &fenceFd);
    ASSERT_EQ(ret, GSERROR_OK);

    struct Region *region = new Region();
    struct Region::Rect *rect = new Region::Rect();
    rect->x = 0x100;
    rect->y = 0x100;
    rect->w = 0x100;
    rect->h = 0x100;
    region->rects = rect;
    ret = OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, nativeWindowBuffer, fenceFd, *region);
    ASSERT_EQ(ret, GSERROR_OK);
    delete region;

    ret = OH_NativeImage_UpdateSurfaceImage(image);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageDetachContext004
 * @tc.desc: test for call OH_NativeImage_DetachContext and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageDetachContext004, Function | MediumTest | Level1)
{
    int32_t ret = OH_NativeImage_DetachContext(image);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageAttachContext004
 * @tc.desc: test for call OH_NativeImage_AttachContext after OH_NativeImage_DetachContext and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageAttachContext004, Function | MediumTest | Level1)
{
    int32_t ret = OH_NativeImage_AttachContext(image, textureId2);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageUpdateSurfaceImage005
 * @tc.desc: test for OHNativeImageUpdateSurfaceImage again and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageUpdateSurfaceImage005, Function | MediumTest | Level1)
{
    NativeWindowBuffer* nativeWindowBuffer = nullptr;
    int fenceFd = -1;
    int32_t ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &nativeWindowBuffer, &fenceFd);
    ASSERT_EQ(ret, GSERROR_OK);

    struct Region *region = new Region();
    struct Region::Rect *rect = new Region::Rect();
    rect->x = 0x100;
    rect->y = 0x100;
    rect->w = 0x100;
    rect->h = 0x100;
    region->rects = rect;
    ret = OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, nativeWindowBuffer, fenceFd, *region);
    ASSERT_EQ(ret, GSERROR_OK);
    delete region;

    ret = OH_NativeImage_UpdateSurfaceImage(image);
    ASSERT_EQ(ret, SURFACE_ERROR_OK);
}

/*
 * @tc.name: OHNativeImageDestroy001
 * @tc.desc: test for call OH_NativeImage_Destroy by abnormal input and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageDestroy001, Function | MediumTest | Level2)
{
    OH_NativeImage_Destroy(nullptr);
    ASSERT_NE(image, nullptr);
}

/*
 * @tc.name: OHNativeImageDestroy002
 * @tc.desc: test for call OH_NativeImage_Destroy and check ret.
 * @tc.type: FUNC
 */
HWTEST_F(NativeImageTest, OHNativeImageDestroy002, Function | MediumTest | Level1)
{
    OH_NativeImage_Destroy(&image);
    ASSERT_EQ(image, nullptr);
}
}