/*-------------------------------------------------------------------------
 * drawElements Quality Program EGL Module
 * ---------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * 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.
 *
 *//*!
 * \file
 * \brief Android-specific operations.
 *//*--------------------------------------------------------------------*/

#include "teglAndroidUtil.hpp"

#include "deStringUtil.hpp"
#include "tcuTextureUtil.hpp"
#include "gluTextureUtil.hpp"
#include "glwEnums.hpp"
#include "eglwLibrary.hpp"
#include "eglwEnums.hpp"

#if (DE_OS == DE_OS_ANDROID)
#	include "tcuAndroidInternals.hpp"
#endif

namespace deqp
{
namespace egl
{
namespace Image
{
using std::string;
using de::MovePtr;
using tcu::PixelBufferAccess;
using tcu::TextureFormat;
using tcu::Texture2D;
using eglu::AttribMap;
using namespace glw;
using namespace eglw;

#if (DE_OS != DE_OS_ANDROID)

MovePtr<ImageSource> createAndroidNativeImageSource	(GLenum format)
{
	return createUnsupportedImageSource("Not Android platform", format);
}

#else // DE_OS == DE_OS_ANDROID

using tcu::Android::internal::LibUI;
using tcu::Android::internal::GraphicBuffer;
using tcu::Android::internal::PixelFormat;
using tcu::Android::internal::status_t;

PixelFormat getPixelFormat (GLenum format)
{
	switch (format)
	{
		case GL_RGB565:		return tcu::Android::internal::PIXEL_FORMAT_RGB_565;
		case GL_RGB8:		return tcu::Android::internal::PIXEL_FORMAT_RGB_888;
		case GL_RGBA4:		return tcu::Android::internal::PIXEL_FORMAT_RGBA_4444;
		case GL_RGB5_A1:	return tcu::Android::internal::PIXEL_FORMAT_RGBA_5551;
		case GL_RGBA8:		return tcu::Android::internal::PIXEL_FORMAT_RGBA_8888;
		default:			TCU_THROW(NotSupportedError, "Texture format unsupported by Android");
	}
}

class AndroidNativeClientBuffer : public ClientBuffer
{
public:
							AndroidNativeClientBuffer	(const LibUI& lib, GLenum format);
	EGLClientBuffer			get							(void) const { return reinterpret_cast<EGLClientBuffer>(m_windowBuffer); }
	GraphicBuffer&			getGraphicBuffer			(void) { return m_graphicBuffer; }

private:
	GraphicBuffer			m_graphicBuffer;
	ANativeWindowBuffer*	m_windowBuffer;
};

AndroidNativeClientBuffer::AndroidNativeClientBuffer (const LibUI& lib, GLenum format)
	: m_graphicBuffer	(lib, 64, 64, getPixelFormat(format),
						 GraphicBuffer::USAGE_SW_READ_OFTEN		|
						 GraphicBuffer::USAGE_SW_WRITE_RARELY	|
						 GraphicBuffer::USAGE_HW_TEXTURE		|
						 GraphicBuffer::USAGE_HW_RENDER)
	, m_windowBuffer	(m_graphicBuffer.getNativeBuffer())
{
}

class AndroidNativeImageSource : public ImageSource
{
public:
							AndroidNativeImageSource	(GLenum format) : m_format(format), m_libui(DE_NULL) {}
							~AndroidNativeImageSource	(void);
	MovePtr<ClientBuffer>	createBuffer 				(const glw::Functions&, Texture2D*) const;
	string					getRequiredExtension		(void) const { return "EGL_ANDROID_image_native_buffer"; }
	EGLImageKHR				createImage					(const Library& egl, EGLDisplay dpy, EGLContext ctx, EGLClientBuffer clientBuffer) const;
	GLenum					getEffectiveFormat			(void) const { return m_format; }

protected:
	GLenum					m_format;

	const LibUI&			getLibUI					(void) const;

private:
	mutable LibUI*			m_libui;
};

AndroidNativeImageSource::~AndroidNativeImageSource (void)
{
	delete m_libui;
}

const LibUI& AndroidNativeImageSource::getLibUI (void) const
{
	if (!m_libui)
		m_libui = new LibUI();

	return *m_libui;
}

void checkStatus (status_t status)
{
	if (status != tcu::Android::internal::OK)
		TCU_FAIL(("Android error: status code " + de::toString(status)).c_str());
}

MovePtr<ClientBuffer> AndroidNativeImageSource::createBuffer (const glw::Functions&, Texture2D* ref) const
{
	MovePtr<AndroidNativeClientBuffer>	buffer			(new AndroidNativeClientBuffer(getLibUI(), m_format));
	GraphicBuffer&						graphicBuffer	= buffer->getGraphicBuffer();
	if (ref != DE_NULL)
	{
		const TextureFormat	texFormat	= glu::mapGLInternalFormat(m_format);
		void*				bufferData	= DE_NULL;

		*ref = Texture2D(texFormat, 64, 64);
		ref->allocLevel(0);
		tcu::fillWithComponentGradients(ref->getLevel(0),
										tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f),
										tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
		checkStatus(graphicBuffer.lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, &bufferData));
		{
			PixelBufferAccess nativeBuffer(texFormat, 64, 64, 1, bufferData);
			tcu::copy(nativeBuffer, ref->getLevel(0));
		}
		checkStatus(graphicBuffer.unlock());
	}
	return MovePtr<ClientBuffer>(buffer);
}

EGLImageKHR AndroidNativeImageSource::createImage (const Library& egl, EGLDisplay dpy, EGLContext, EGLClientBuffer clientBuffer) const
{
	static const EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
	const EGLImageKHR	image 		= egl.createImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attribs);

	EGLU_CHECK_MSG(egl, "eglCreateImageKHR()");
	return image;
}

MovePtr<ImageSource> createAndroidNativeImageSource	(GLenum format)
{
	try
	{
		return MovePtr<ImageSource>(new AndroidNativeImageSource(format));
	}
	catch (const std::runtime_error& exc)
	{
		return createUnsupportedImageSource(string("Android native buffers unsupported: ") + exc.what(), format);
	}
}

#endif // DE_OS == DE_OS_ANDROID

} // Image
} // egl
} // deqp