public static enum

ConfigChooser.ConfigChooserMatcher

extends Enum<E extends Enum<E>>
package org.andengine.opengl.view;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;

import android.opengl.GLSurfaceView;

/**
 * (c) Zynga 2011
 *
 * @author Nicolas Gramlich <ngramlich@zynga.com>
 * @since 15:31:48 - 04.08.2011
 */
public class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
	// ===========================================================
	// Constants
	// ===========================================================

	private static final int[] BUFFER = new int[1];

	private static final int RED_SIZE = 5;
	private static final int GREEN_SIZE = 6;
	private static final int BLUE_SIZE = 5;
	private static final int DEPTH_SIZE = 0;
	private static final int ALPHA_SIZE = 0;
	private static final int STENCIL_SIZE = 0;

	private static final int MULTISAMPLE_COUNT = 2; // TODO Could be made variable?

	private static final int EGL_GLES2_BIT = 4;

	private static final int[] EGLCONFIG_ATTRIBUTES_MULTISAMPLE = {
		EGL10.EGL_RED_SIZE, ConfigChooser.RED_SIZE,
		EGL10.EGL_GREEN_SIZE, ConfigChooser.GREEN_SIZE,
		EGL10.EGL_BLUE_SIZE, ConfigChooser.BLUE_SIZE,
		EGL10.EGL_ALPHA_SIZE, ConfigChooser.ALPHA_SIZE,
		EGL10.EGL_DEPTH_SIZE, ConfigChooser.DEPTH_SIZE,
		EGL10.EGL_STENCIL_SIZE, ConfigChooser.STENCIL_SIZE,
		EGL10.EGL_RENDERABLE_TYPE, ConfigChooser.EGL_GLES2_BIT,
		EGL10.EGL_SAMPLE_BUFFERS, 1,
		EGL10.EGL_SAMPLES, ConfigChooser.MULTISAMPLE_COUNT,
		EGL10.EGL_NONE
	};

	private static final int EGL_COVERAGE_BUFFERS_NV = 0x30E0;
	private static final int EGL_COVERAGE_SAMPLES_NV = 0x30E1;

	private static final int[] EGLCONFIG_ATTRIBUTES_COVERAGEMULTISAMPLE_NVIDIA = new int[]{
		EGL10.EGL_RED_SIZE, ConfigChooser.RED_SIZE,
		EGL10.EGL_GREEN_SIZE, ConfigChooser.GREEN_SIZE,
		EGL10.EGL_BLUE_SIZE, ConfigChooser.BLUE_SIZE,
		EGL10.EGL_ALPHA_SIZE, ConfigChooser.ALPHA_SIZE,
		EGL10.EGL_DEPTH_SIZE, ConfigChooser.DEPTH_SIZE,
		EGL10.EGL_STENCIL_SIZE, ConfigChooser.STENCIL_SIZE,
		EGL10.EGL_RENDERABLE_TYPE, ConfigChooser.EGL_GLES2_BIT,
		ConfigChooser.EGL_COVERAGE_BUFFERS_NV, 1,
		ConfigChooser.EGL_COVERAGE_SAMPLES_NV, ConfigChooser.MULTISAMPLE_COUNT,  // always 5 in practice on tegra 2
		EGL10.EGL_NONE
	};

	private static final int[] EGLCONFIG_ATTRIBUTES_FALLBACK = new int[] {
		EGL10.EGL_RED_SIZE, ConfigChooser.RED_SIZE,
		EGL10.EGL_GREEN_SIZE, ConfigChooser.GREEN_SIZE,
		EGL10.EGL_BLUE_SIZE, ConfigChooser.BLUE_SIZE,
		EGL10.EGL_ALPHA_SIZE, ConfigChooser.ALPHA_SIZE,
		EGL10.EGL_DEPTH_SIZE, ConfigChooser.DEPTH_SIZE,
		EGL10.EGL_STENCIL_SIZE, ConfigChooser.STENCIL_SIZE,
		EGL10.EGL_RENDERABLE_TYPE, ConfigChooser.EGL_GLES2_BIT,
		EGL10.EGL_NONE
	};

	// ===========================================================
	// Fields
	// ===========================================================

	private final boolean mMultiSamplingRequested;

	private boolean mMultiSampling;
	private boolean mCoverageMultiSampling;

	private int mRedSize = -1;
	private int mGreenSize = -1;
	private int mBlueSize = -1;
	private int mAlphaSize = -1;
	private int mDepthSize = -1;
	private int mStencilSize = -1;

	// ===========================================================
	// Constructors
	// ===========================================================

	public ConfigChooser(final boolean pMultiSamplingRequested) {
		this.mMultiSamplingRequested = pMultiSamplingRequested;
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	public boolean isMultiSampling() {
		return this.mMultiSampling;
	}

	public boolean isCoverageMultiSampling() {
		return this.mCoverageMultiSampling;
	}

	public int getRedSize() {
		return this.mRedSize;
	}

	public int getGreenSize() {
		return this.mGreenSize;
	}

	public int getBlueSize() {
		return this.mBlueSize;
	}

	public int getAlphaSize() {
		return this.mAlphaSize;
	}

	public int getDepthSize() {
		return this.mDepthSize;
	}

	public int getStencilSize() {
		return this.mStencilSize;
	}

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	@Override
	public EGLConfig chooseConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay) {
		try {
			return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.STRICT);
		} catch (final IllegalArgumentException e) {

		}

		try {
			return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.LOOSE_STENCIL);
		} catch (final IllegalArgumentException e) {

		}

		try {
			return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.LOOSE_DEPTH_AND_STENCIL);
		} catch (final IllegalArgumentException e) {

		}

		return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.ANY);
	}

	private EGLConfig chooseConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final ConfigChooserMatcher pConfigChooserMatcher) throws IllegalArgumentException {
		ConfigChooser.BUFFER[0] = 0;

		int eglConfigCount;

		if(this.mMultiSamplingRequested) {
			eglConfigCount = this.getEGLConfigCount(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_MULTISAMPLE);
			if(eglConfigCount > 0) {
				this.mMultiSampling = true;
				return this.findEGLConfig(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_MULTISAMPLE, eglConfigCount, pConfigChooserMatcher);
			}

			eglConfigCount = this.getEGLConfigCount(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_COVERAGEMULTISAMPLE_NVIDIA);
			if(eglConfigCount > 0) {
				this.mCoverageMultiSampling = true;
				return this.findEGLConfig(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_COVERAGEMULTISAMPLE_NVIDIA, eglConfigCount, pConfigChooserMatcher);
			}
		}

		eglConfigCount = this.getEGLConfigCount(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_FALLBACK);
		if(eglConfigCount > 0) {
			return this.findEGLConfig(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_FALLBACK, eglConfigCount, pConfigChooserMatcher);
		} else {
			throw new IllegalArgumentException("No " + EGLConfig.class.getSimpleName() + " found!");
		}
	}

	// ===========================================================
	// Methods
	// ===========================================================

	private int getEGLConfigCount(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final int[] pEGLConfigAttributes) {
		if(pEGL.eglChooseConfig(pEGLDisplay, pEGLConfigAttributes, null, 0, ConfigChooser.BUFFER) == false) {
			throw new IllegalArgumentException("EGLCONFIG_FALLBACK failed!");
		}
		return ConfigChooser.BUFFER[0];
	}

	private EGLConfig findEGLConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final int[] pEGLConfigAttributes, final int pEGLConfigCount, final ConfigChooserMatcher pConfigChooserMatcher) {
		final EGLConfig[] eglConfigs = new EGLConfig[pEGLConfigCount];
		if(!pEGL.eglChooseConfig(pEGLDisplay, pEGLConfigAttributes, eglConfigs, pEGLConfigCount, ConfigChooser.BUFFER)) {
			throw new IllegalArgumentException("findEGLConfig failed!");
		}

		return this.findEGLConfig(pEGL, pEGLDisplay, eglConfigs, pConfigChooserMatcher);
	}

	private EGLConfig findEGLConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final EGLConfig[] pEGLConfigs, final ConfigChooserMatcher pConfigChooserMatcher) {
		for(int i = 0; i < pEGLConfigs.length; ++i) {
			final EGLConfig config = pEGLConfigs[i];
			if(config != null) {
				final int redSize = this.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_RED_SIZE, 0);
				final int greenSize = this.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_GREEN_SIZE, 0);
				final int blueSize = this.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_BLUE_SIZE, 0);
				final int alphaSize = this.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_ALPHA_SIZE, 0);
				final int depthSize = this.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_DEPTH_SIZE, 0);
				final int stencilSize = this.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_STENCIL_SIZE, 0);

				if(pConfigChooserMatcher.matches(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize)) {
					this.mRedSize = redSize;
					this.mGreenSize = greenSize;
					this.mBlueSize = blueSize;
					this.mAlphaSize = alphaSize;
					this.mDepthSize = depthSize;
					this.mStencilSize = stencilSize;
					return config;
				}
			}
		}
		throw new IllegalArgumentException("No EGLConfig found!");
	}

	private int getConfigAttrib(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final EGLConfig pEGLConfig, final int pAttribute, final int pDefaultValue) {
		if(pEGL.eglGetConfigAttrib(pEGLDisplay, pEGLConfig, pAttribute, ConfigChooser.BUFFER)) {
			return ConfigChooser.BUFFER[0];
		} else {
			return pDefaultValue;
		}
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================

	public enum ConfigChooserMatcher {
		// ===========================================================
		// Elements
		// ===========================================================

		STRICT() {
			@Override
			public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
				if(pDepthSize == ConfigChooser.DEPTH_SIZE && pStencilSize == ConfigChooser.STENCIL_SIZE) {
					if(pRedSize == ConfigChooser.RED_SIZE && pGreenSize == ConfigChooser.GREEN_SIZE && pBlueSize == ConfigChooser.BLUE_SIZE && pAlphaSize == ConfigChooser.ALPHA_SIZE) {
						return true;
					}
				}
				return false;
			}
		},
		LOOSE_STENCIL() {
			@Override
			public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
				if(pDepthSize == ConfigChooser.DEPTH_SIZE && pStencilSize >= ConfigChooser.STENCIL_SIZE) {
					if(pRedSize == ConfigChooser.RED_SIZE && pGreenSize == ConfigChooser.GREEN_SIZE && pBlueSize == ConfigChooser.BLUE_SIZE && pAlphaSize == ConfigChooser.ALPHA_SIZE) {
						return true;
					}
				}
				return false;
			}
		},
		LOOSE_DEPTH_AND_STENCIL() {
			@Override
			public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
				if(pDepthSize >= ConfigChooser.DEPTH_SIZE && pStencilSize >= ConfigChooser.STENCIL_SIZE) {
					if(pRedSize == ConfigChooser.RED_SIZE && pGreenSize == ConfigChooser.GREEN_SIZE && pBlueSize == ConfigChooser.BLUE_SIZE && pAlphaSize == ConfigChooser.ALPHA_SIZE) {
						return true;
					}
				}
				return false;
			}
		},
		ANY() {
			@Override
			public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
				return true;
			}
		};

		// ===========================================================
		// Constants
		// ===========================================================

		// ===========================================================
		// Fields
		// ===========================================================

		// ===========================================================
		// Constructors
		// ===========================================================

		// ===========================================================
		// Getter & Setter
		// ===========================================================

		// ===========================================================
		// Methods for/from SuperClass/Interfaces
		// ===========================================================

		public abstract boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize);

		// ===========================================================
		// Methods
		// ===========================================================

		// ===========================================================
		// Inner and Anonymous Classes
		// ===========================================================
	}
}