/*
 * Copyright (c) 2002-2011 LWJGL Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'LWJGL' nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.lwjgl.opengl;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;
import org.lwjgl.PointerBuffer;
import org.lwjgl.opengles.ContextAttribs;
import org.lwjgl.opengles.*;
import org.lwjgl.opengles.Util;

import static org.lwjgl.opengles.EGL.*;
import static org.lwjgl.opengles.GLES20.*;

/**
 * @author Spasi
 * @since 14/5/2011
 */
abstract class DrawableGLES implements DrawableLWJGL {

	/** The PixelFormat used to create the EGLDisplay. */
	protected org.lwjgl.opengles.PixelFormat pixel_format;

	protected EGLDisplay eglDisplay;
	protected EGLConfig  eglConfig;
	protected EGLSurface eglSurface;

	/** The OpenGL Context. */
	protected ContextGLES context;

	/** The Drawable that shares objects with this Drawable. */
	protected Drawable shared_drawable;

	protected DrawableGLES() {
	}

	public void setPixelFormat(final PixelFormatLWJGL pf) throws LWJGLException {
		synchronized ( GlobalLock.lock ) {
			this.pixel_format = (org.lwjgl.opengles.PixelFormat)pf;
		}
	}

	public PixelFormatLWJGL getPixelFormat() {
		synchronized ( GlobalLock.lock ) {
			return pixel_format;
		}
	}

	public void initialize(final long window, final long display_id, final int eglSurfaceType, final org.lwjgl.opengles.PixelFormat pf) throws LWJGLException {
		synchronized ( GlobalLock.lock ) {
			if ( eglSurface != null ) {
				eglSurface.destroy();
				eglSurface = null;
			}

			if ( eglDisplay != null ) {
				eglDisplay.terminate();
				eglDisplay = null;
			}

			final EGLDisplay eglDisplay = eglGetDisplay((int)display_id);

			int[] attribs = {
				EGL_LEVEL, 0,
				EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
				EGL_NATIVE_RENDERABLE, EGL_FALSE,
			};

			final EGLConfig[] configs = eglDisplay.chooseConfig(pf.getAttribBuffer(eglDisplay, eglSurfaceType, attribs), null, BufferUtils.createIntBuffer(1));
			if ( configs.length == 0 )
				throw new LWJGLException("No EGLConfigs found for the specified PixelFormat.");

			final EGLConfig eglConfig = pf.getBestMatch(configs);
			final EGLSurface eglSurface = eglDisplay.createWindowSurface(eglConfig, window, null);
			pf.setSurfaceAttribs(eglSurface);

			this.eglDisplay = eglDisplay;
			this.eglConfig = eglConfig;
			this.eglSurface = eglSurface;

			// This can happen when switching in and out of full-screen mode.
			if ( context != null )
				context.getEGLContext().setDisplay(eglDisplay);
		}
	}

	public void createContext(final ContextAttribs attribs, final Drawable shared_drawable) throws LWJGLException {
		synchronized ( GlobalLock.lock ) {
			this.context = new ContextGLES(this, attribs, shared_drawable != null ? ((DrawableGLES)shared_drawable).getContext() : null);
			this.shared_drawable = shared_drawable;
		}
	}

	Drawable getSharedDrawable() {
		synchronized ( GlobalLock.lock ) {
			return shared_drawable;
		}
	}

	public EGLDisplay getEGLDisplay() {
		synchronized ( GlobalLock.lock ) {
			return eglDisplay;
		}
	}

	public EGLConfig getEGLConfig() {
		synchronized ( GlobalLock.lock ) {
			return eglConfig;
		}
	}

	public EGLSurface getEGLSurface() {
		synchronized ( GlobalLock.lock ) {
			return eglSurface;
		}
	}

	public ContextGLES getContext() {
		synchronized ( GlobalLock.lock ) {
			return context;
		}
	}

	public org.lwjgl.opengl.Context createSharedContext() throws LWJGLException {
		synchronized ( GlobalLock.lock ) {
			checkDestroyed();
			return new ContextGLES(this, context.getContextAttribs(), context);
		}
	}

	public void checkGLError() {
		Util.checkGLError();
	}

	public void setSwapInterval(final int swap_interval) {
		ContextGLES.setSwapInterval(swap_interval);
	}

	public void swapBuffers() throws LWJGLException {
		ContextGLES.swapBuffers();
	}

	public void initContext(final float r, final float g, final float b) {
		// set background clear color
		glClearColor(r, g, b, 0.0f);
		// Clear window to avoid the desktop "showing through"
		glClear(GL_COLOR_BUFFER_BIT);
	}

	public boolean isCurrent() throws LWJGLException {
		synchronized ( GlobalLock.lock ) {
			checkDestroyed();
			return context.isCurrent();
		}
	}

	public void makeCurrent() throws LWJGLException, PowerManagementEventException {
		synchronized ( GlobalLock.lock ) {
			checkDestroyed();
			context.makeCurrent();
		}
	}

	public void releaseContext() throws LWJGLException, PowerManagementEventException {
		synchronized ( GlobalLock.lock ) {
			checkDestroyed();
			if ( context.isCurrent() )
				context.releaseCurrent();
		}
	}

	public void destroy() {
		synchronized ( GlobalLock.lock ) {
			try {
				if ( context != null ) {
					try {
						releaseContext();
					} catch (PowerManagementEventException e) {
						// Ignore
					}

					context.forceDestroy();
					context = null;
				}

				if ( eglSurface != null ) {
					eglSurface.destroy();
					eglSurface = null;
				}

				if ( eglDisplay != null ) {
					eglDisplay.terminate();
					eglDisplay = null;
				}

				pixel_format = null;
				shared_drawable = null;
			} catch (LWJGLException e) {
				LWJGLUtil.log("Exception occurred while destroying Drawable: " + e);
			}
		}
	}

	protected void checkDestroyed() {
		if ( context == null )
			throw new IllegalStateException("The Drawable has no context available.");
	}

	public void setCLSharingProperties(final PointerBuffer properties) throws LWJGLException {
		throw new UnsupportedOperationException();
	}

}