package org.cocos2d;


import static javax.microedition.khronos.opengles.GL10.GL_COLOR_ARRAY;
import static javax.microedition.khronos.opengles.GL10.GL_DITHER;
import static javax.microedition.khronos.opengles.GL10.GL_FASTEST;
import static javax.microedition.khronos.opengles.GL10.GL_ONE_MINUS_SRC_ALPHA;
import static javax.microedition.khronos.opengles.GL10.GL_PERSPECTIVE_CORRECTION_HINT;
import static javax.microedition.khronos.opengles.GL10.GL_SRC_ALPHA;
import static javax.microedition.khronos.opengles.GL10.GL_TEXTURE_2D;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import org.cocos2d.actions.CCActionManager;
import org.cocos2d.base_nodes.CCNode;
import org.cocos2d.events.CCKeyDispatcher;
import org.cocos2d.events.CCTouchDispatcher;
import org.cocos2d.include.ccConfig;
import org.cocos2d.include.ccMacros;
import org.cocos2d.kazmath.kmMat4;
import org.cocos2d.kazmath.kmVec3;
import org.cocos2d.kazmath.GL.Matrix;
import org.cocos2d.layers_scenes_transitions_nodes.CCScene;
import org.cocos2d.nodes.CCLabelAtlas;
import org.cocos2d.nodes.CCSpriteFrameCache;
import org.cocos2d.nodes.CCLabel.TextAlignment;
import org.cocos2d.opengl.CCBitmapFontAtlas;
import org.cocos2d.opengl.GLResourceHelper;
import org.cocos2d.opengl.GLSurfaceView;
import org.cocos2d.platform.CCApplication;
import org.cocos2d.shaders.ccGLStateCache;
import org.cocos2d.support.CCPointExtension;
import org.cocos2d.textures.CCTexture2D;
import org.cocos2d.textures.CCTextureCache;
import org.cocos2d.transitions.CCTransitionScene;
import org.cocos2d.cocoa.CCGeometry.CCPoint;
import org.cocos2d.cocoa.CCGeometry.CCRect;
import org.cocos2d.cocoa.CCGeometry.CCSize;
import org.cocos2d.types.util.CGPointUtil;
import org.cocos2d.utils.CCFormatter;
import org.cocos2d.utils.javolution.TextBuilder;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.opengl.GLES20;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

/**
 * @addtogroup base_nodes
 * @{
 */

/**
 * @brief Class that creates and handle the main Window and manages how
 * and when to execute the Scenes.
 *  
 *  The CCDirector is also responsible for:
 *   - initializing the OpenGL context
 *   - setting the OpenGL pixel format (default on is RGB565)
 *   - setting the OpenGL buffer depth (default one is 0-bit)
 *   - setting the projection (default one is 3D)
 *   - setting the orientation (default one is Portrait)
 *  
 *  Since the CCDirector is a singleton, the standard way to use it is by calling:
 *   _ CCDirector::sharedDirector()->methodName();
 *  
 *  The CCDirector also sets the default OpenGL context:
 *   - GL_TEXTURE_2D is enabled
 *   - GL_VERTEX_ARRAY is enabled
 *   - GL_COLOR_ARRAY is enabled
 *   - GL_TEXTURE_COORD_ARRAY is enabled
 */
public abstract class CCDirector implements GLSurfaceView.Renderer {
	private static final String TAG = CCDirector.class.getSimpleName();

	/** @typedef ccDirectorProjection
	 * Possible OpenGL projections used by director
	 */
	public enum ccDirectorProjection {
		/// sets a 2D projection (orthogonal projection)
		kCCDirectorProjection2D,
		/// sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500.
		kCCDirectorProjection3D,
		/// it calls "updateProjection" on the projection delegate.
		kCCDirectorProjectionCustom,
	}
	/// Default projection is 3D projection
	public static final ccDirectorProjection kCCDirectorProjectionDefault = ccDirectorProjection.kCCDirectorProjection3D;

	public CCDirector() {
	}

	public boolean init() {
		ccMacros.CCLOG(TAG, "cocos2d: " + Cocos2D.cocos2dVersion());

		// scenes
		m_pRunningScene = null;
		m_pNextScene = null;

		m_pNotificationNode = null;

		m_dOldAnimationInterval = m_dAnimationInterval = 1.0 / kDefaultFPS;
		m_pobScenesStack = Collections.synchronizedList(new ArrayList<CCScene>(10));

		// Set default projection (3D)
		m_eProjection = kCCDirectorProjectionDefault;

		// projection delegate if "Custom" projection is used
// TODO		m_pProjectionDelegate = null;

		// FPS
		m_fAccumDt = 0.0f;
		m_fFrameRate = 0.0f;
		m_pFPSLabel = null;
		m_pSPFLabel = null;
		m_pDrawsLabel = null;
		m_bDisplayStats = false;
		m_uTotalFrames = m_uFrames = 0;
		m_pszFPS = new String();
		m_pLastUpdate = 0;

		// paused ?
		m_bPaused = false;

		// purge ?
		m_bPurgeDirecotorInNextLoop = false;

		m_obWinSizeInPoints.setSize(CCSize.Zero);

// TODO CCEGLView		m_pobOpenGLView = null;
		openGLView_ = null;

		m_fContentScaleFactor = 1.0f;

/* TODO
		// scheduler
		m_pScheduler = new CCScheduler();
		// action manager
		m_pActionManager = new CCActionManager();
		m_pScheduler.scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);
		// touchDispatcher
		m_pTouchDispatcher = new CCTouchDispatcher();
		m_pTouchDispatcher.init();

		// KeypadDispatcher
		m_pKeypadDispatcher = new CCKeypadDispatcher();

		// Accelerometer
		m_pAccelerometer = new CCAccelerometer();
*/

		// TODO legacy -->

//		synchronized (CCDirector.class) {
		ccMacros.CCLOG(TAG, "cocos2d: Using Director Type:" + this.getClass());

		// default values
		pixelFormat_ = kCCPixelFormatDefault;
		depthBufferFormat_ = 0;

		//Create a full-screen window

		// landscape
		deviceOrientation_ = kCCDeviceOrientationPortrait;

		// FPS
		displayFPS = false;
		m_uFrames = 0;

		m_obWinSizeInPoints.setSize(CCSize.Zero);
		surfaceSize_.setSize(CCSize.Zero);
		isContentScaleSupported_ = false;
//		}

		// TODO <-- legacy

		return true;
	}

	// attribute

	/** Get current running Scene. Director can only run one Scene at the time */
	public CCScene getRunningScene() {
		return m_pRunningScene;
	}

	/** Get the FPS value */
	public double getAnimationInterval() {
		return m_dAnimationInterval;
	}

	/** Set the FPS value. */
	public abstract void setAnimationInterval(double dValue);

	/** Whether or not to display the FPS on the bottom-left corner */
	public boolean isDisplayStats() {
		return m_bDisplayStats;
	}

	/** Display the FPS on the bottom-left corner */
	public void setDisplayStats(boolean bDisplayStats) {
		m_bDisplayStats = bDisplayStats;
	}

	/** seconds per frame */
	public float getSecondsPerFrame() {
		return m_fSecondsPerFrame;
	}

	/** Get the CCEGLView, where everything is rendered */
/* TODO CCEGLView
	public CCEGLView getOpenGLView(void) {
		return m_pobOpenGLView;
	}
*/
	public GLSurfaceView getOpenGLView() {
		return openGLView_;
	}

/* TODO CCEGLView
    public void setOpenGLView(CCEGLView pobOpenGLView) {
        assert null != pobOpenGLView : "opengl view should not be null";
        
        if (m_pobOpenGLView != pobOpenGLView) {
            // EAGLView is not a CCObject
            m_pobOpenGLView = pobOpenGLView;
            
            // set size
            m_obWinSizeInPoints = m_pobOpenGLView.getDesignResolutionSize();
            
            createStatsLabel();
            
            if(null != m_pobOpenGLView) {
                setGLDefaultValues();
            }
            
            CHECK_GL_ERROR_DEBUG();
            
            m_pobOpenGLView.setTouchDelegate(m_pTouchDispatcher);
            m_pTouchDispatcher.setDispatchEvents(true);
        }
    }
    
    public void setOpenGLView(GLSurfaceView view) {
        assert view!=null: "EAGView must be non-nil";
        
        if( view != openGLView_ ) {
            openGLView_ = view;
            
            // set size
            m_obWinSizeInPoints = CGSize.make(view.getWidth(), view.getHeight());
            surfaceSize_ = CGSize.make(m_obWinSizeInPoints.width * contentScaleFactor_, m_obWinSizeInPoints.height *contentScaleFactor_);
            
            CCTouchDispatcher touchDispatcher = CCTouchDispatcher.sharedDispatcher();
            openGLView_.setTouchDelegate(touchDispatcher);
            touchDispatcher.setDispatchEvents(true);

            if( contentScaleFactor_ != 1 )
                updateContentScaleFactor();

            setGLDefaultValues();
        }
    }
*/

	public boolean isNextDeltaTimeZero() {
		return m_bNextDeltaTimeZero;
	}

	public void setNextDeltaTimeZero(boolean bNextDeltaTimeZero) {
		m_bNextDeltaTimeZero = bNextDeltaTimeZero;
	}

	/** Whether or not the Director is paused */
	public boolean isPaused() {
		return m_bPaused;
	}

	/** How many frames were called since the director started */
	public int getTotalFrames() {
		return m_uTotalFrames;
	}

	/** Sets an OpenGL projection
	 * @since v0.8.2
	 */

	public ccDirectorProjection getProjection() {
		return m_eProjection;
	}

	public void setProjection(ccDirectorProjection kProjection) {
		CCSize size = m_obWinSizeInPoints;

		setViewport();

		switch (kProjection) {
		case kCCDirectorProjection2D:
			{
				Matrix.kmGLMatrixMode(Matrix.KM_GL_PROJECTION);
				Matrix.kmGLLoadIdentity();
				kmMat4 orthoMatrix = new kmMat4();
				kmMat4.kmMat4OrthographicProjection(orthoMatrix, 0, size.width, 0, size.height, -1024, 1024);
				Matrix.kmGLMultMatrix(orthoMatrix);
				Matrix.kmGLMatrixMode(Matrix.KM_GL_MODELVIEW);
				Matrix.kmGLLoadIdentity();
			}
			break;

		case kCCDirectorProjection3D:
			{
				float zeye = this.getZEye();

				kmMat4 matrixPerspective = new kmMat4();
				kmMat4 matrixLookup = new kmMat4();

				Matrix.kmGLMatrixMode(Matrix.KM_GL_PROJECTION);
				Matrix.kmGLLoadIdentity();

				// issue #1334
				kmMat4.kmMat4PerspectiveProjection(matrixPerspective, 60, size.width/size.height, 0.1f, zeye*2);
				// kmMat4.kmMat4PerspectiveProjection(matrixPerspective, 60, size.width/size.height, 0.1f, 1500);

				Matrix.kmGLMultMatrix(matrixPerspective);

				Matrix.kmGLMatrixMode(Matrix.KM_GL_MODELVIEW);
				Matrix.kmGLLoadIdentity();
				kmVec3 eye = new kmVec3();
				kmVec3 center = new kmVec3();
				kmVec3 up = new kmVec3();
				kmVec3.kmVec3Fill(eye, size.width/2, size.height/2, zeye );
				kmVec3.kmVec3Fill(center, size.width/2, size.height/2, 0.0f );
				kmVec3.kmVec3Fill(up, 0.0f, 1.0f, 0.0f);
				kmMat4.kmMat4LookAt(matrixLookup, eye, center, up);
				Matrix.kmGLMultMatrix(matrixLookup);
			}
			break;

		case kCCDirectorProjectionCustom:
/* TODO ProjectionDelegate
			if(m_pProjectionDelegate != null) {
				m_pProjectionDelegate.updateProjection();
			}
*/
			break;

		default:
			ccMacros.CCLOG(TAG, "cocos2d: Director: unrecognized projecgtion");
			break;
		}
		m_eProjection = kProjection;
		ccGLStateCache.ccSetProjectionMatrixDirty();
	}

	/** Sets the glViewport*/
	public void setViewport() {
/* TODO CCEGLView
		if(m_pobOpenGLView) {
			m_pobOpenGLView.setViewPortInPoints(0, 0, m_obWinSizeInPoints.width, m_obWinSizeInPoints.height);
		}
*/
	}

	/** How many frames were called since the director started */

	/** Whether or not the replaced scene will receive the cleanup message.
	 * If the new scene is pushed, then the old scene won't receive the "cleanup" message.
	 * If the new scene replaces the old one, the it will receive the "cleanup" message.
	 * @since v0.99.0
	 */
	public boolean isSendCleanupToScene() {
		return m_bSendCleanupToScene;
	}

	/** This object will be visited after the main scene is visited.
	 * This object MUST implement the "visit" selector.
	 * Useful to hook a notification object, like CCNotifications (http://github.com/manucorporat/CCNotifications)
	 * @since v0.99.5
	 */
	public CCNode getNotificationNode() {
		return m_pNotificationNode;
	}

	public void setNotificationNode(CCNode node) {
		m_pNotificationNode = node;
	}

	/** CCDirector delegate. It shall implemente the CCDirectorDelegate protocol
	 * @since v0.99.5
	 */
/* TODO CCDirectorDelegate
	public CCDirectorDelegate getDelegate() {
		return m_pProjectionDelegate;
	}

	public void setDelegate(CCDirectorDelegate pDelegate) {
		m_pProjectionDelegate = pDelegate;
	}
*/

	// window size

	/** returns the size of the OpenGL view in points.
	 */
	public CCSize getWinSize() {
		return m_obWinSizeInPoints;
	}

	/** returns the size of the OpenGL view in pixels.
	 */
	public CCSize getWinSizeInPixels() {
		return CCSize.Make(m_obWinSizeInPoints.width  * m_fContentScaleFactor, m_obWinSizeInPoints.height * m_fContentScaleFactor);
	}

	/** returns visible size of the OpenGL view in points.
	 *  the value is equal to getWinSize if don't invoke
	 *  CCEGLView::setDesignResolutionSize()
	 */
	public CCSize getVisibleSize() {
/* TODO CCEGLView
		if(m_pobOpenGLView != null) {
			return m_pobOpenGLView->getVisibleSize();
		} else {
			return CGSize.zero();
		}
*/
		if(openGLView_ != null) {
			return CCSize.Make(openGLView_.getWidth(), openGLView_.getHeight());
		} else {
			return CCSize.Zero;
		}
	}

	/** returns visible origin of the OpenGL view in points.
	 */
	public CCPoint getVisibleOrigin() {
/* TODO CCEGLView
		if(null != m_pobOpenGLView) {
			return m_pobOpenGLView->getVisibleOrigin();
		} else {
			return CGPoint.zero();
		}
*/
		if(openGLView_ != null) {
			return CCPoint.Make(openGLView_.getWidth(), openGLView_.getHeight());
		} else {
			return CCPoint.Zero;
		}
	}

	private static void GLToClipTransform(kmMat4 transformOut) {
		kmMat4 projection = new kmMat4();
		Matrix.kmGLGetMatrix(Matrix.KM_GL_PROJECTION, projection);

		kmMat4 modelview = new kmMat4();
		Matrix.kmGLGetMatrix(Matrix.KM_GL_MODELVIEW, modelview);

		kmMat4.kmMat4Multiply(transformOut, projection, modelview);
	}

	/** converts a UIKit coordinate to an OpenGL coordinate
	 * Useful to convert (multi) touch coordinates to the current layout (portrait or landscape)
	 */
	public CCPoint convertToGL(final CCPoint obPoint) {
/* TODO
		kmMat4 transform = new kmMat4();
		GLToClipTransform(transform);

		kmMat4 transformInv = new kmMat4();
		kmMat4.kmMat4Inverse(transformInv, transform);

		// Calculate z=0 using -> transform*[0, 0, 0, 1]/w
		float zClip = transform.mat[14]/transform.mat[15];

		CCSize glSize = new CCSize(m_pobOpenGLView.getDesignResolutionSize());
		kmVec3 clipCoord = {2.0f*uiPoint.x/glSize.width - 1.0f, 1.0f - 2.0f*uiPoint.y/glSize.height, zClip};

		kmVec3 glCoord = new kmVec3();
		kmVec3.kmVec3TransformCoord(glCoord, clipCoord, transformInv);

		return CCPointExtension.ccp(glCoord.x, glCoord.y);
*/
		// TODO legacy -->
		float newX = obPoint.x / surfaceSize_.width * m_obWinSizeInPoints.width;
		float newY = m_obWinSizeInPoints.height - obPoint.y / surfaceSize_.height * m_obWinSizeInPoints.height;

		CCPoint ret = null;
		switch (deviceOrientation_) {
		case kCCDeviceOrientationPortrait:
			ret = CCPointExtension.ccp(newX, newY);
			break;

		case kCCDeviceOrientationLandscapeLeft:
			ret = CCPointExtension.ccp(newX, newY);
			break;

		default:
			return ret;
		}

		if (m_fContentScaleFactor != 1 && isContentScaleSupported_ )
			ret = CCPointExtension.ccpMult(ret, m_fContentScaleFactor);
		return ret;
		// TODO <-- legacy
	}

	/** converts an OpenGL coordinate to a UIKit coordinate
	 * Useful to convert node points to window points for calls such as glScissor
	 */
	public CCPoint convertToUI(final CCPoint obPoint) {
/* TODO
		kmMat4 transform;
		GLToClipTransform(&transform);

		kmVec3 clipCoord;
		// Need to calculate the zero depth from the transform.
		kmVec3 glCoord = {glPoint.x, glPoint.y, 0.0};
		kmVec3TransformCoord(&clipCoord, &glCoord, &transform);

		CCSize glSize = m_pobOpenGLView->getDesignResolutionSize();
		return ccp(glSize.width*(clipCoord.x*0.5 + 0.5), glSize.height*(-clipCoord.y*0.5 + 0.5));
*/
		// TODO legacy -->
		CCSize winSize = surfaceSize_;
		int oppositeY = (int)(winSize.height - obPoint.y);
		
		CCPoint uiPoint = null;
		switch ( deviceOrientation_) {
		case kCCDeviceOrientationPortrait:
			uiPoint = CCPointExtension.ccp(obPoint.x, oppositeY);
			//uiPoint = CGPoint.ccp(oppositeY, glPoint.x);
			break;

		case kCCDeviceOrientationLandscapeLeft:
			// uiPoint = CGPoint.ccp(glPoint.y, glPoint.x);
			uiPoint = CCPointExtension.ccp(obPoint.x, oppositeY);
			break;
		default:
			return null;
		}

		uiPoint = CCPointExtension.ccpMult(uiPoint, 1/m_fContentScaleFactor);
		return uiPoint;
		// TODO <-- legacy
	}

	/// XXX: missing description
	public float getZEye () {
		return (m_obWinSizeInPoints.height / 1.1566f);
	}

	// Scene Management

	/**Enters the Director's main loop with the given Scene. 
	 * Call it to run only your FIRST scene.
	 * Don't call it if there is already a running scene.
	 *
	 * It will call pushScene: and then it will call startAnimation
	 */
	public void runWithScene(CCScene pScene) {
		assert pScene != null : "This command can only be used to start the CCDirector. There is already a scene present.";
		assert m_pRunningScene == null : "m_pRunningScene should be null";

		pushScene(pScene);
		startAnimation();
	}

	/**Suspends the execution of the running scene, pushing it on the stack of suspended scenes.
	 * The new scene will be executed.
	 * Try to avoid big stacks of pushed scenes to reduce memory allocation. 
	 * ONLY call it if there is a running scene.
	 */
	public void pushScene(CCScene pScene) {
		assert pScene != null : "the scene should not null";

		m_bSendCleanupToScene = false;

		m_pobScenesStack.add(pScene);
		m_pNextScene = pScene;
	}

	/**Pops out a scene from the queue.
	 * This scene will replace the running one.
	 * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated.
	 * ONLY call it if there is a running scene.
	 */
	public void popScene() {
		assert m_pRunningScene != null : "running scene should not null";

		m_pobScenesStack.remove(m_pobScenesStack.size() - 1);
		int c = m_pobScenesStack.size();

		if (c == 0) {
			end();
		} else {
			m_bSendCleanupToScene = true;
			m_pNextScene = m_pobScenesStack.get(c - 1);
		}
	}

	/**Pops out all scenes from the queue until the root scene in the queue.
	 * This scene will replace the running one.
	 * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated.
	 * ONLY call it if there is a running scene.
	 */
	public void popToRootScene() {
		ccMacros.CCAssert(m_pRunningScene != null, "A running Scene is needed");
		int c = m_pobScenesStack.size();

		if(c == 1) {
			m_pobScenesStack.remove(0);
			this.end();
		} else {
/* TODO
			while(c > 1) {
				CCScene current = m_pobScenesStack.lastObject();
				if(current.isRunning()) {
					current.onExitTransitionDidStart();
					current.onExit();
				}
				current.cleanup();

				m_pobScenesStack.removeLastObject();
				c--;
			}
			m_pNextScene = m_pobScenesStack.lastObject();
			m_bSendCleanupToScene = false;
*/
		}
	}

	/** Replaces the running scene with a new one. The running scene is terminated.
	 * ONLY call it if there is a running scene.
	 */
	public void replaceScene(CCScene pScene) {
		assert m_pRunningScene != null : "Use runWithScene: instead to start the director";
		assert pScene != null : "the scene should not be null";

		int index = m_pobScenesStack.size();

		m_bSendCleanupToScene = true;
		m_pobScenesStack.set(index - 1, pScene);

		m_pNextScene = pScene;
	}

	/** Ends the execution, releases the running scene.
	 * It doesn't remove the OpenGL view from its parent. You have to do it manually.
	 */
	public void end() {
		m_bPurgeDirecotorInNextLoop = true;
	}

	/** Pauses the running scene.
	 * The running scene will be _drawed_ but all scheduled timers will be paused
	 * While paused, the draw rate will be 4 FPS to reduce CPU consumption
	 */
	public void pause() {
		if(m_bPaused) {
			return;
		}

		m_dOldAnimationInterval = m_dAnimationInterval;

		// when paused, don't consume CPU
		setAnimationInterval(1.0 / 4.0);
		m_bPaused = true;
	}

	/** Resumes the paused scene
	 * The scheduled timers will be activated again.
	 * The "delta time" will be 0 (as if the game wasn't paused)
	 */
	public void resume() {
		if(! m_bPaused) {
			return;
		}

		setAnimationInterval(m_dOldAnimationInterval);

		m_pLastUpdate = System.currentTimeMillis();

		m_bPaused = false;
		m_fDeltaTime = 0;
	}

	/** Stops the animation. Nothing will be drawn. The main loop won't be triggered anymore.
	 * If you don't want to pause your animation call [pause] instead.
	 */
	public abstract void stopAnimation();

	/** The main loop is triggered again.
	 * Call this function only if [stopAnimation] was called earlier
	 * @warning Don't call this function to start the main loop. To run the main loop call runWithScene
	 */
	public abstract void startAnimation();

	/** Draw the scene.
	 * This method is called every frame. Don't call it manually.
	 */
	public void drawScene() {
		// calculate "global" dt
		calculateDeltaTime();

		//tick before glClear: issue #533
		if(! m_bPaused) {
			// TODO m_pScheduler->update(m_fDeltaTime);
			CCScheduler.sharedScheduler().tick(m_fDeltaTime);
		}

		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

		// TODO legacy
		GLResourceHelper.sharedHelper().update(gl);

		/* to avoid flickr, nextScene MUST be here: after tick and before draw.
		 XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
		if(m_pNextScene != null) {
			setNextScene();
		}

		Matrix.kmGLPushMatrix();

		// TODO legacy -->
		applyOrientation(gl);

		// By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D
		ccMacros.CC_ENABLE_DEFAULT_GL_STATES(gl);
		// TODO <-- legacy

		// draw the scene
		if(m_pRunningScene != null) {
			m_pRunningScene.visit();
		}

		// draw the notifications node
		if(m_pNotificationNode != null) {
			m_pNotificationNode.visit();
		}

		if(m_bDisplayStats) {
			showStats();
		}

		// TODO legacy -->
		if( displayFPS )
			showFPS();

		if (ccConfig.CC_ENABLE_PROFILERS != 0) {
			// showProfilers();
		}

		ccMacros.CC_DISABLE_DEFAULT_GL_STATES(gl);
		// TODO <-- legacy

		Matrix.kmGLPopMatrix();

		m_uTotalFrames++;

		// swap buffers
/* TODO
		if(m_pobOpenGLView != null) {
			m_pobOpenGLView.swapBuffers();
		}
*/
		if(m_bDisplayStats) {
			calculateMPF();
		}
	}

	// Memory Helper

	/** Removes cached all cocos2d cached data.
	 * It will purge the CCTextureCache, CCSpriteFrameCache, CCLabelBMFont cache
	 * @since v0.99.3
	 */
	public void purgeCachedData() {
/* TODO
		CCLabelBMFont.purgeCachedData();
		if(s_SharedDirector->getOpenGLView() != null) {
			CCTextureCache.sharedTextureCache().removeUnusedTextures();
		}
		CCFileUtils.sharedFileUtils().purgeCachedEntries();
*/
		// TODO legacy -->
		CCBitmapFontAtlas.purgeCachedData();
		CCSpriteFrameCache.purgeSharedSpriteFrameCache();
		CCTextureCache.purgeSharedTextureCache();
		// TODO <-- legacy
	}

	// OpenGL Helper

	/** sets the OpenGL default values */
	public void setGLDefaultValues() {
		// This method SHOULD be called only after openGLView_ was initialized
		ccMacros.CCAssert(openGLView_ != null, "opengl view should not be null");

		setAlphaBlending(true);
		// XXX: Fix me, should enable/disable depth test according the depth format as cocos2d-iphone did
		// [self setDepthTest: view_.depthFormat];
		setDepthTest(false);
		// setProjection(m_eProjection); is called in onSurfaceChanged()

		// set other opengl default values
		GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

		// TODO legacy -->
		if (ccConfig.CC_DIRECTOR_FAST_FPS) {
			createStatsLabel();
			/*
			if (m_pFPSLabel==null) {
				// CCTexture2DPixelFormat currentFormat = CCTexture2D.defaultAlphaPixelFormat();
				// CCTexture2D.setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_RGBA4444);
				m_pFPSLabel = CCLabelAtlas.label("00.0", "fps_images.png", 16, 24, '.');
				m_pFPSLabel.setPosition(50, 2);
			}
			*/
		}	// CC_DIRECTOR_FAST_FPS
		// TODO <-- legacy
	}

	/** enables/disables OpenGL alpha blending */
	public void setAlphaBlending(boolean bOn) {
		if (bOn) {
			ccGLStateCache.ccGLBlendFunc(ccMacros.CC_BLEND_SRC, ccMacros.CC_BLEND_DST);
		} else {
			ccGLStateCache.ccGLBlendFunc(GLES20.GL_ONE, GLES20.GL_ZERO);
		}

		ccMacros.CHECK_GL_ERROR_DEBUG();
	}

	/** enables/disables OpenGL depth test */
	public void setDepthTest(boolean bOn) {
		if (bOn){
			GLES20.glClearDepthf(1.0f);
			GLES20.glEnable(GLES20.GL_DEPTH_TEST);
			GLES20.glDepthFunc(GLES20.GL_LEQUAL);
//			GLES20.glHint(GLES20.GL_PERSPECTIVE_CORRECTION_HINT, GLES20.GL_NICEST);
		} else {
			GLES20.glDisable(GLES20.GL_DEPTH_TEST);
		}
		ccMacros.CHECK_GL_ERROR_DEBUG();
	}

	public abstract void mainLoop();

	/** The size in pixels of the surface. It could be different than the screen size.
	 * High-res devices might have a higher surface size than the screen size.
	 * Only available when compiled using SDK >= 4.0.
	 * @since v0.99.4
	 */
	public void setContentScaleFactor(GL10 gl, float scaleFactor) {
		if(scaleFactor != m_fContentScaleFactor) {
			m_fContentScaleFactor = scaleFactor;
			createStatsLabel();

			// TODO legacy->>
			surfaceSize_.setSize(m_obWinSizeInPoints.width * scaleFactor, m_obWinSizeInPoints.height * scaleFactor );

			if(openGLView_!= null) {
				this.updateContentScaleFactor();
			}

			// update projection
			setProjection(m_eProjection);
			// TODO <-- legacy
		}
	}

	public float getContentScaleFactor() {
		return m_fContentScaleFactor;
	}

	/** CCScheduler associated with this director
	 * @since v2.0
	 */
	protected CCScheduler m_pScheduler;

	public CCScheduler getScheduler() {
		return m_pScheduler;
	}

	public void setScheduler(CCScheduler pScheduler) {
		if(m_pScheduler != pScheduler) {
			m_pScheduler = pScheduler;
		}
	}

	/** CCActionManager associated with this director
	 * @since v2.0
	 */
	protected CCActionManager m_pActionManager;

	public CCActionManager getActionManager() {
		return m_pActionManager;
	}

	public void setActionManager(CCActionManager pActionManager) {
		if(m_pActionManager != pActionManager) {
			m_pActionManager = pActionManager;
		}
	}

	/** CCTouchDispatcher associated with this director
	 * @since v2.0
	 */
	protected CCTouchDispatcher m_pTouchDispatcher;

	public CCTouchDispatcher getTouchDispatcher() {
		return m_pTouchDispatcher;
	}

	public void setTouchDispatcher(CCTouchDispatcher pTouchDispatcher) {
		if(m_pTouchDispatcher != pTouchDispatcher) {
			m_pTouchDispatcher = pTouchDispatcher;
		}
	}

	/** CCKeypadDispatcher associated with this director
	 * @since v2.0
	 */
/* TODO CCKeypadDispatcher
	protected CCKeypadDispatcher m_pKeypadDispatcher;

	public CCKeypadDispatcher getKeypadDispatcher() {
		return m_pKeypadDispatcher;
	}

	public void setKeypadDispatcher(CCKeypadDispatcher pKeypadDispatcher) {
		m_pKeypadDispatcher = pKeypadDispatcher;
	}
*/

	/** CCAccelerometer associated with this director
	 @since v2.0
	 */
/* TODO CCAccelerometer
	protected CCAccelerometer m_pAccelerometer;

	public CCAccelerometer getAccelerometer() {
		return m_pAccelerometer;
	}

	public void setAccelerometer(CCAccelerometer pAccelerometer) {
		if(m_pAccelerometer != pAccelerometer) {
			m_pAccelerometer = pAccelerometer;
		}
	}
*/

	/* delta time since last tick to main loop */
	protected float m_fDeltaTime;

	public float getDeltaTime() {
		return m_fDeltaTime;
	}

	/** returns a shared instance of the director */
	public static CCDirector sharedDirector() {
		synchronized(CCDirector.class) {
			if(s_SharedDirector == null) {
				s_SharedDirector = new CCDisplayLinkDirector();
				s_SharedDirector.init();
			}
			return s_SharedDirector;
		}
	}

	protected void purgeDirector() {
/* TODO
		// cleanup scheduler
		getScheduler().unscheduleAll();
*/
		// don't release the event handlers
		// They are needed in case the director is run again
// TODO		m_pTouchDispatcher.removeAllDelegates();
		CCTouchDispatcher.sharedDispatcher().removeAllDelegates();

		if(m_pRunningScene != null) {
			m_pRunningScene.onExitTransitionDidStart();
			m_pRunningScene.onExit();
			m_pRunningScene.cleanup();
		}

		m_pRunningScene = null;
		m_pNextScene = null;

		// remove all objects, but don't release it.
		// runWithScene might be executed after 'end'.
		m_pobScenesStack.clear();

		stopAnimation();

		m_pFPSLabel = null;
		m_pSPFLabel = null;
		m_pDrawsLabel = null;

		// purge bitmap cache
// TODO		CCLabelBMFont.purgeCachedData();
		CCBitmapFontAtlas.purgeCachedData();

		// purge all managed caches
// TODO		ccDrawFree();
// TODO		CCAnimationCache.purgeSharedAnimationCache();
		CCSpriteFrameCache.purgeSharedSpriteFrameCache();
		CCTextureCache.purgeSharedTextureCache();
// TODO		CCShaderCache.purgeSharedShaderCache();
// TODO		CCFileUtils.purgeFileUtils();
// TODO		CCConfiguration.purgeConfiguration();
/* TODO
		// cocos2d-x specific data structures
		CCUserDefault.purgeSharedUserDefault();
		CCNotificationCenter.purgeNotificationCenter();
*/
		ccGLStateCache.ccGLInvalidateStateCache();

		ccMacros.CHECK_GL_ERROR_DEBUG();
/* TODO
		// OpenGL view
		m_pobOpenGLView.end();
		m_pobOpenGLView = null;
*/
		// delete CCDirector
		synchronized(CCDirector.class) {
			s_SharedDirector = null;
		}
	}

	protected boolean m_bPurgeDirecotorInNextLoop; // this flag will be set to true in end()

	protected void setNextScene() {
		boolean runningIsTransition = m_pRunningScene instanceof CCTransitionScene;
		boolean newIsTransition = m_pNextScene instanceof CCTransitionScene;

		// If it is not a transition, call onExit
		if(! newIsTransition) {
			if(m_pRunningScene != null) {
				m_pRunningScene.onExitTransitionDidStart();
				m_pRunningScene.onExit();
			}

			// issue #709. the root node (scene) should receive the cleanup message too
			// otherwise it might be leaked.
			if(m_bSendCleanupToScene && (m_pRunningScene != null)) {
				m_pRunningScene.cleanup();
			}
		}

		m_pRunningScene = m_pNextScene;
		m_pNextScene = null;

		if((! runningIsTransition) && (m_pRunningScene != null)) {
			m_pRunningScene.onEnter();
			m_pRunningScene.onEnterTransitionDidFinish();
		}
	}

	protected void showStats() {
		m_uFrames++;
		m_fAccumDt += m_fDeltaTime;

		if(m_bDisplayStats) {
			if((m_pFPSLabel != null) && (m_pSPFLabel != null) && (m_pDrawsLabel != null)) {
				if(m_fAccumDt > ccConfig.CC_DIRECTOR_STATS_INTERVAL) {
					m_pSPFLabel.setString(CCFormatter.format("%.3f", m_fSecondsPerFrame));

					m_fFrameRate = m_uFrames / m_fAccumDt;
					m_uFrames = 0;
					m_fAccumDt = 0;

					m_pFPSLabel.setString(CCFormatter.format("%.1f", m_fFrameRate));
					m_pDrawsLabel.setString(CCFormatter.format("%4d", g_uNumberOfDraws));
				}

//				m_pDrawsLabel.draw();
				m_pFPSLabel.draw();
//				m_pSPFLabel.draw();
			}
		}

		g_uNumberOfDraws = 0;
	}

	protected void createStatsLabel() {

		m_pFPSLabel = CCLabelAtlas.create("00.0", "fps_images.png", 16, 24, '.');
		m_pSPFLabel = CCLabelAtlas.create("0.000", "fps_images.png", 16, 24, '.');
		m_pDrawsLabel = CCLabelAtlas.create("000", "fps_images.png", 16, 24, '.');

		CCSize contentSize;
		contentSize = m_pDrawsLabel.getContentSize();
		m_pDrawsLabel.setPosition(CCPointExtension.ccpAdd(CCPointExtension.ccp(contentSize.width/2, contentSize.height*5/2), getVisibleOrigin()));
		contentSize = m_pSPFLabel.getContentSize();
		m_pSPFLabel.setPosition(CCPointExtension.ccpAdd(CCPointExtension.ccp(contentSize.width/2, contentSize.height*3/2), getVisibleOrigin()));
		contentSize = m_pFPSLabel.getContentSize();
		m_pFPSLabel.setPosition(CCPointExtension.ccpAdd(CCPointExtension.ccp(contentSize.width/2, contentSize.height/2), getVisibleOrigin()));

/* TODO
		if((m_pFPSLabel != null) && (m_pSPFLabel != null)) {
			if(m_pFPSLabel != null) m_pFPSLabel = null;
			if(m_pSPFLabel != null) m_pSPFLabel = null;
			if(m_pDrawsLabel != null) m_pDrawsLabel = null;
			CCFileUtils.sharedFileUtils().purgeCachedEntries();
		}

		int fontSize = 0;
		if(m_obWinSizeInPoints.width > m_obWinSizeInPoints.height) {
			fontSize = (int)(m_obWinSizeInPoints.height / 320.0f * 24);
		} else {
			fontSize = (int)(m_obWinSizeInPoints.width / 320.0f * 24);
		}

		m_pFPSLabel = CCLabelTTF.create("00.0", "Arial", fontSize);
		m_pSPFLabel = CCLabelTTF.create("0.000", "Arial", fontSize);
		m_pDrawsLabel = CCLabelTTF.create("000", "Arial", fontSize);

		CGSize contentSize = m_pDrawsLabel.getContentSize();
		m_pDrawsLabel.setPosition(ccpAdd(CGPoint.ccp(contentSize.width/2, contentSize.height*5/2), CC_DIRECTOR_STATS_POSITION));
		contentSize = m_pSPFLabel.getContentSize();
		m_pSPFLabel.setPosition(ccpAdd(CGPoint.ccp(contentSize.width/2, contentSize.height*3/2), CC_DIRECTOR_STATS_POSITION));
		contentSize = m_pFPSLabel.getContentSize();
		m_pFPSLabel.setPosition(ccpAdd(CGPoint.ccp(contentSize.width/2, contentSize.height/2), CC_DIRECTOR_STATS_POSITION));
*/
	}

	protected void calculateMPF() {
		long now = System.currentTimeMillis();

		m_fSecondsPerFrame = (now - m_pLastUpdate) + (now - m_pLastUpdate) / 1000000.0f;
	}

/* TODO Unnecessary?
	protected void getFPSImageData(unsigned char** datapointer, unsigned int* length) {
		// XXX fixed me if it should be used 
//		*datapointer = cc_fps_images_png;
//		*length = cc_fps_images_len();
	}
*/

	/** calculates delta time since last time it was called */
	protected void calculateDeltaTime() {
		long now = System.currentTimeMillis();

		// new delta time. Re-fixed issue #1277
		if(m_bNextDeltaTimeZero) {
			m_fDeltaTime = 0;
			m_bNextDeltaTimeZero = false;
		} else {
			// TODO m_fDeltaTime = (now - m_pLastUpdate) + (now - m_pLastUpdate) / 1000000.0f;
			m_fDeltaTime = (now - m_pLastUpdate) * 0.001f;
			m_fDeltaTime = Math.max(0, m_fDeltaTime);
		}

		m_pLastUpdate = now;
	}

	/* The CCEGLView, where everything is rendered */
// TODO    protected CCEGLView m_pobOpenGLView;
	protected GLSurfaceView openGLView_;

	protected double m_dAnimationInterval;
	protected double m_dOldAnimationInterval;

	/* landscape mode ? */
	protected boolean m_bLandscape;

	protected boolean m_bDisplayStats;
	protected float m_fAccumDt;
	protected float m_fFrameRate;

// TODO	protected CCLabelTTF m_pFPSLabel;
// TODO	protected CCLabelTTF m_pSPFLabel;
// TODO	protected CCLabelTTF m_pDrawsLabel;
	protected CCLabelAtlas m_pFPSLabel;
	protected CCLabelAtlas m_pSPFLabel;
	protected CCLabelAtlas m_pDrawsLabel;

	/** Whether or not the Director is paused */
	protected boolean m_bPaused;

	/* How many frames were called since the director started */
	protected int m_uTotalFrames;
	protected int m_uFrames;
	protected float m_fSecondsPerFrame;

	/* The running scene */
	protected CCScene m_pRunningScene;

	/* will be the next 'runningScene' in the next frame
	 nextScene is a weak reference. */
	protected CCScene m_pNextScene;

	/* If YES, then "old" scene will receive the cleanup message */
	protected boolean	m_bSendCleanupToScene;

	/* scheduled scenes */
	protected List<CCScene> m_pobScenesStack;

	/* last time the main loop was updated */
	protected long m_pLastUpdate;

	/* whether or not the next delta time will be zero */
	protected boolean m_bNextDeltaTimeZero;

	/* projection used */
	protected ccDirectorProjection m_eProjection;

	/* window size in points */
	protected CCSize m_obWinSizeInPoints = new CCSize();

	/* content scale factor */
	protected float m_fContentScaleFactor;

	/* store the fps string */
	protected String m_pszFPS;

	/* This object will be visited after the scene. Useful to hook a notification node */
	protected CCNode m_pNotificationNode;

	/* Projection protocol delegate */
// TODO	protected CCDirectorDelegate m_pProjectionDelegate;

	/**
	 Position of the FPS
	 
	 Default: 0,0 (bottom-left corner)
	 */
	private static CCPoint CC_DIRECTOR_STATS_POSITION() {
		return CCDirector.sharedDirector().getVisibleOrigin();
	}

	public static int g_uNumberOfDraws = 0;

	// singleton stuff
	private static CCDirector s_SharedDirector;

	private static final double kDefaultFPS = 60.0; // 60 frames per second


	// TODO legacy -->
    
    /** @typedef tPixelFormat
      Possible Pixel Formats for the EAGLView
      */
    /** RGB565 pixel format. No alpha. 16-bit. (Default) */
    public static final int kCCPixelFormatRGB565 = 0;
    /** RGBA format. 32-bit. Needed for some 3D effects. It is not as fast as the RGB565 format. */
    public static final int kCCPixelFormatRGBA8888 = 1;
    /** default pixel format */
    public static final int kCCPixelFormatDefault = kCCPixelFormatRGB565;

    /** @typedef tDepthBufferFormat
      Possible DepthBuffer Formats for the EAGLView.
      Use 16 or 24 bit depth buffers if you are going to use real 3D objects.
      */
    /// A Depth Buffer of 0 bits will be used (default)
    public static final int kCCDepthBufferNone = 0;
    /// A depth buffer of 16 bits will be used
    public static final int kCCDepthBuffer16 = 1;
    /// A depth buffer of 24 bits will be used
    public static final int kCCDepthBuffer24 = 2;

    /** @typedef ccDirectorType
      Possible Director Types.
      @since v0.8.2
      */
    /** Will use a Director that triggers the main loop from an NSTimer object
     *
     * Features and Limitations:
     * - Integrates OK with UIKit objects
     * - It the slowest director
     * - The invertal update is customizable from 1 to 60
     */
    public static final int kCCDirectorTypeNSTimer = 1;

    /** will use a Director that triggers the main loop from a custom main loop.
     *
     * Features and Limitations:
     * - Faster than NSTimer Director
     * - It doesn't integrate well with UIKit objecgts
     * - The interval update can't be customizable
     */
    public static final int kCCDirectorTypeMainLoop = 2;

    /** Will use a Director that triggers the main loop from a thread, but the main loop will be executed on the main thread.
     *
     * Features and Limitations:
     * - Faster than NSTimer Director
     * - It doesn't integrate well with UIKit objecgts
     * - The interval update can't be customizable
     */
    public static final int kCCDirectorTypeThreadMainLoop = 3;

    /** Will use a Director that synchronizes timers with the refresh rate of the display.
     *
     * Features and Limitations:
     * - Faster than NSTimer Director
     * - Only available on 3.1+
     * - Scheduled timers and drawing are synchronizes with the refresh rate of the display
     * - Integrates OK with UIKit objects
     * - The interval update can be 1/60, 1/30, 1/15
     */	
    public static final int kCCDirectorTypeDisplayLink = 4;

    /** Default director is the NSTimer directory */
    public static final int kCCDirectorTypeDefault = kCCDirectorTypeNSTimer;

    /** @typedef ccDeviceOrientation
      Possible device orientations
      */
        /// Device oriented vertically, home button on the bottom
    public static final int kCCDeviceOrientationPortrait = Configuration.ORIENTATION_PORTRAIT;

    /// Device oriented vertically, home button on the top
    ///    kCCDeviceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,

        /// Device oriented horizontally, home button on the right
    public static final int kCCDeviceOrientationLandscapeLeft = Configuration.ORIENTATION_LANDSCAPE;

    /// Device oriented horizontally, home button on the left
    ///    kCCDeviceOrientationLandscapeRight = UIDeviceOrientationLandscapeRight;

    public static Activity theApp = null;

    public Activity getActivity() {
        return theApp;
    }

    // Landscape is right or left ?
    // private static final boolean LANDSCAPE_LEFT = false;

    // Fast FPS display. FPS are updated 10 times per second without consuming resources
    // uncomment this line to use the old method that updated
    private static final boolean FAST_FPS_DISPLAY = true;


    private int depthBufferFormat_;
    /** Change depth buffer format of the render buffer.
      Call this class method before attaching it to a UIWindow/UIView
      Default depth buffer: 0 (none).  Supported: kCCDepthBufferNone, kCCDepthBuffer16, and kCCDepthBuffer24

      @deprecated Set the depth buffer format when creating the EAGLView. This method will be removed in v1.0
      */
    public void setDepthBufferFormat(int db) {
	    assert (!isOpenGLAttached()):"Can't change the depth buffer format after the director was initialized";
        depthBufferFormat_ = db;
    }

    /** Pixel format used to create the context */
    private int pixelFormat_;

    public int getPixelFormat() {
        return pixelFormat_;
    }


    /** Uses a new pixel format for the EAGLView.
      Call this class method before attaching it to a UIView
      Default pixel format: kRGB565. Supported pixel formats: kRGBA8 and kRGB565

      @deprecated Set the pixel format when creating the EAGLView. This method will be removed in v1.0
    */
    @Deprecated
    public void setPixelFormat(int p) {
	    assert (!this.isOpenGLAttached()):"Can't change the pixel format after the director was initialized";	
        pixelFormat_ = p;
    }

    /* orientation */
    int	deviceOrientation_;

    /** The device orientattion */
    public int getDeviceOrientation() {
        return deviceOrientation_;
    }

    public void setDeviceOrientation(int orientation) {
        if( deviceOrientation_ != orientation ) {
            deviceOrientation_ = orientation;
            switch( deviceOrientation_) {
                case kCCDeviceOrientationPortrait:
                    theApp.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    break;
                case kCCDeviceOrientationLandscapeLeft:
                    theApp.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    break;
                default:
                    Log.w(TAG, "Director: Unknown device orientation");
                    break;
            }
        }
    }

    // rotates the screen if an orientation differnent than Portrait is used
    private void applyOrientation(GL10 gl) {
        CCSize s = surfaceSize_;
         float h = s.height / 2;
         float w = s.width / 2;

        // XXX it's using hardcoded values.
        // What if the the screen size changes in the future?
        switch ( deviceOrientation_ ) {
            case kCCDeviceOrientationPortrait:
                // nothing
                break;
            case kCCDeviceOrientationLandscapeLeft:
                // gl.glTranslatef(w, h, 0);
                // gl.glRotatef(-90, 0, 0, 1);
                // gl.glTranslatef(-h, -w, 0);
                break;
        }
    }

    /**
     * The OpenGL view
     */

	/* screen, different than surface size */
	private CCSize	screenSize_ = new CCSize();
	// change m_obWinSizeInPoints

	/* screen, different than surface size */
	private CCSize	surfaceSize_ = new CCSize();
	


	/* contentScaleFactor could be simulated */
	private boolean isContentScaleSupported_;

	private float accumDtForProfiler_;

    /**
     * Whether or not to display the FPS on the bottom-left corner
     */
	/* display FPS ? */
    private boolean displayFPS;

    public void setDisplayFPS(boolean value) {
        displayFPS = value;
    }

    /** There are 4 types of Director.
      - kCCDirectorTypeNSTimer (default)
      - kCCDirectorTypeMainLoop
      - kCCDirectorTypeThreadMainLoop
      - kCCDirectorTypeDisplayLink

      Each Director has it's own benefits, limitations.
      If you are using SDK 3.1 or newer it is recommed to use the DisplayLink director

      This method should be called before any other call to the director.

      It will return NO if the director type is kCCDirectorTypeDisplayLink and the running SDK is < 3.1. Otherwise it will return YES.

      @since v0.8.2
      */
    /*public static boolean setDirectorType(int directorType) {
        assert _sharedDirector==null 
            : "A Director was alloced. setDirectorType must be the first call to Director";

        
        if( type == CCDirectorTypeDisplayLink ) {
            NSString *reqSysVer = @"3.1";
            NSString *currSysVer = [[UIDevice currentDevice] systemVersion];

            if([currSysVer compare:reqSysVer options:NSNumericSearch] == NSOrderedAscending)
                return NO;
        }

	    switch (directorType) {
		case CCDirectorTypeNSTimer:
            CCTimerDirector.sharedDirector();
			break;
		case CCDirectorTypeDisplayLink:
            CCDisplayLinkDirector.sharedDirector();
			break;
		case CCDirectorTypeMainLoop:
            CCFastDirector.sharedDirector();
			break;
		case CCDirectorTypeThreadMainLoop:
            CCThreadedFastDirector.sharedDirector();
			break;
		default:
			assert false: "Unknown director type";
	    }

    	return true;
    }*/


//	public void useFaseDirector(){
//		assert _sharedDirector == null : "A Director was allocated. setDirectorType must be the first call to Director";
//
//		FastDirector.sharedDirector();
//	}


    /*
    public void finalize() {
        ccMacros.CCLOG(LOG_TAG, "cocos2d: deallocing " + this);

        if (ccConfig.CC_DIRECTOR_FAST_FPS) {
            FPSLabel_ = null;
        }

        runningCCScene_ = null;
        CCScenesStack_ = null;

        _sharedDirector = null;
    }*/

    public void onSurfaceChanged(GL10 gl, int width, int height) {
    	CCDirector.gl = gl;
    	surfaceSize_.setSize(width, height);
        gl.glViewport(0, 0, width, height);
        setProjection(m_eProjection);
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    	CCDirector.gl = gl;

        /*
         * By default, OpenGL enables features that improve quality
         * but reduce performance. One might want to tweak that
         * especially on software renderer.
         */
        gl.glDisable(GL_DITHER);

        /*
        * Some one-time OpenGL initialization can be made here
        * probably based on features of this particular context
        */
        gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);

        setGLDefaultValues(); 

        // save thread id
        GLResourceHelper.sharedHelper().setGlThreadID( Thread.currentThread().getId() );
        
    	// reload all GL resources here
    	GLResourceHelper.sharedHelper().reloadResources();    	
    }

    public void onDrawFrame(GL10 gl) {
//        synchronized (this) {
//            this.notify();
//        }
//        Thread.yield();
//        
//        synchronized(this) {
//		if (_sharedDirector == null)
//		return;
		
    	GLResourceHelper.sharedHelper().setInUpdate(true);
    	
		CCTouchDispatcher.sharedDispatcher().update();
		//added by Ishaq 
		CCKeyDispatcher.sharedDispatcher().update();
		drawScene();
		
		GLResourceHelper.sharedHelper().setInUpdate(false);
		
		waitForFPS();
//        }
    }    

    private double sleepInterval = 0;
    
	private void waitForFPS() {
		if (m_dAnimationInterval >= m_fDeltaTime) {
			sleepInterval = m_dAnimationInterval - m_fDeltaTime + sleepInterval;
			SystemClock.sleep( (long)(sleepInterval * 1000) );

		} else {
			sleepInterval = 0;
		}
	}



    
    
    public CCSize winSizeRef() {
    	return m_obWinSizeInPoints;
    }

    /** returns the display size of the OpenGL view in pixels */
    public CCSize displaySize() {
        return CCSize.Make(surfaceSize_.width, surfaceSize_.height);
    }
    
    public boolean getLandscape() {
        return deviceOrientation_ == kCCDeviceOrientationLandscapeLeft;
    }

    public void setLandscape(boolean on) {
        if ( on )
            setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);
        else
            setDeviceOrientation(kCCDeviceOrientationPortrait);
    }

    // Director Integration with a Android view
    // is the view currently attached
    public boolean isOpenGLAttached() {
        return true;
        // return openGLView_.getParent() != null;
    }

    /** detach the cocos2d view from the view/window */
    @Deprecated
    private boolean detach() {

        // detach or attach to a view or a window
        assert isOpenGLAttached():
        "FATAL: Director: Can't detach the OpenGL View, because it is not attached. Attach it first.";

       // remove from the superview
       ViewGroup vg = (ViewGroup)openGLView_.getParent();
       vg.removeView(vg);
       
       assert (!isOpenGLAttached()) :
       "FATAL: Director: Can't detach the OpenGL View, it is still attached to the superview.";


       return true;
    }

    /** attach in UIWindow using the full frame.
      It will create a EAGLView.
      @deprecated set setOpenGLView instead. Will be removed in v1.0
      */
    @Deprecated
    public boolean attachInWindow(View window) {
        return attachInView(window);
        /*
        if([self initOpenGLViewWithView:window withFrame:[window bounds]])
        {
            return YES;
        }
        
        return NO;
        */
    }

    /** attach in UIView using the full frame.
      It will create a EAGLView.

      deprecated set setOpenGLView instead. Will be removed in v1.0
    */
    public boolean attachInView(View view) {

//        CCRect rect = new CCRect(view.getScrollX(), view.getScrollY(), view.getWidth(), view.getHeight());
        WindowManager w = theApp.getWindowManager();
        CCRect rect = new CCRect(0, 0, w.getDefaultDisplay().getWidth(), w.getDefaultDisplay().getHeight());
        // CGRect rect = CGRect.make(view.getLeft(), view.getBottom(), view.getWidth(), view.getHeight());
        
        return initOpenGLViewWithView(view, rect);

        /*
        if([self initOpenGLViewWithView:view withFrame:[view bounds]])
        {
            return YES;
        }
        
        return NO;
        */
    }

    /** attach in UIView using the given frame.
      It will create a EAGLView and use it.

      @deprecated set setOpenGLView instead. Will be removed in v1.0
    */
    @Deprecated
    public boolean attachInView(View view, CCRect rect) {
        return initOpenGLViewWithView(view, rect);

        /*
        if([self initOpenGLViewWithView:view withFrame:frame])
        {
            return YES;
        }
        
        return NO;
        */
    }
    
    /**
     * attach in UIView using the given frame, and ration.
     * ratio = width / height
     * It is the easiest way to port from iPhone ration 480f/320f 
     * to any android device but not the best.
     * It will create a EAGLView and use it.
     * 
     * @param view
     * @param ration
     * @return
     */
    
    public boolean attachInView(View view, float ration)
    {
    	return initOpenGLViewWithView(view, getAppFrameRect(ration));
    }
    
    private CCRect getAppFrameRect(float targetRatio)
    {
    	 WindowManager w = theApp.getWindowManager();
    	 CCSize size = CCSize.Make(w.getDefaultDisplay().getWidth(), w.getDefaultDisplay().getHeight());
         
         float currentRation = size.width / size.height;
         CCSize newSize = CCSize.Make(size.width, size.height);
         CCPoint offset = CCPoint.Make(0, 0);
         
         if (currentRation > targetRatio)
         {
        	 newSize.width = targetRatio * size.height;
        	 offset.x = (size.width - newSize.width) / 2;
         }
         	else if (currentRation < targetRatio)
         {
         		newSize.height = size.width / targetRatio;
         		offset.y = (size.height - newSize.height) / 2;
         }
         
         CCRect rect = new CCRect(offset, newSize);
         
         return rect;
    }
    
	public void setScreenSize(float width, float height) {
		m_obWinSizeInPoints.setSize(width, height);
	}
	
    private boolean initOpenGLViewWithView(View view, CCRect rect) {
        surfaceSize_.setSize(rect.size);
        m_obWinSizeInPoints.setSize(surfaceSize_);
        
//        try {
        if (openGLView_ != view) {
        	openGLView_ = (GLSurfaceView) view;
        	openGLView_.setRenderer(this);
//        	openGLView_.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
        }
//
//            // check if the view is not attached
//            if(isOpenGLAttached())
//            {
//                // the view is already attached
//                throw new OpenGLViewAlreadyAttached("Can't re-attach the OpenGL View, because it is already attached. Detach it first.");
//                return false;
//            }
//
//            // check if the view is not initialized
        if (openGLView_ != null) {
//                openGLView_.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

//                // define the pixel format
//                NSString	*pFormat = kEAGLColorFormatRGB565;
//                GLuint		depthFormat = 0;
//
//                if(pixelFormat_==kRGBA8)
//                    pFormat = kEAGLColorFormatRGBA8;
//
//                // alloc and init the opengl view
//                openGLView_ = [[EAGLView alloc] initWithFrame:rect pixelFormat:pFormat depthFormat:depthFormat preserveBackbuffer:NO];
//
//                // check if the view was alloced and initialized
//                if(openGLView_ == null)
//                {
//                    // the view was not created
//                    throw new OpenGLViewCantInit("Could not alloc and init the OpenGL View.");
//                }
//
//                // set autoresizing enabled when attaching the glview to another view
//                openGLView_.setAutoresizesEAGLSurface(true);
//
//                // set the touch delegate of the glview to self
//                openGLView_.setTouchDelegate(this);
        } else {
//                // set the (new) frame of the glview
//                openGLView_.setFrame(rect);
        }
//
//            // check if the superview has touchs enabled and enable it in our view
//            if([view isUserInteractionEnabled])
//            {
//                [openGLView_ setUserInteractionEnabled:YES];

        CCTouchDispatcher.sharedDispatcher().setDispatchEvents(true);

//            }
//            else
//            {
//                [openGLView_ setUserInteractionEnabled:NO];
//                setEventsEnabled(false);
//            }
//
//            // check if multi touches are enabled and set them
//            if([view isMultipleTouchEnabled])
//            {
//                [openGLView_ setMultipleTouchEnabled:YES];
//            }
//            else
//            {
//                [openGLView_ setMultipleTouchEnabled:NO];
//            }
//
//            // add the glview to his (new) superview
//            [view.addSubview(openGLView_);
//
//            // set the background color of the glview
//            //	[backgroundColor setOpenGLClearColor];
//
//            // check if the glview is attached now
//            if(isOpenGLAttached())
//            {
//                initGLDefaultValues();
//                return true;
//            }
//
//            // the glview is not attached, but it should have been
//            throw new OpenGLViewCantAttach("Can't attach the OpenGL View.");
//
//        } catch (Exception e) {
//        }
        return true;
    }

    public void showProfilers() {
        accumDtForProfiler_ += m_fDeltaTime;
        if (accumDtForProfiler_ > 1.0f) {
            accumDtForProfiler_ = 0;
            // CCProfiler.sharedProfiler().displayTimers();
        }
    }


    
    /**
     * Optimizaed version of convertToGL(CGPoint uiPoint).
     * @param uiPoint - point for conversion
     * @param ret - result point
     */
    public void convertToGL(CCPoint uiPoint, CCPoint ret) {
    	convertToGL(uiPoint.x, uiPoint.y, ret);
    }
    
    /**
     * Optimizaed version of convertToGL(CGPoint uiPoint).
     * @param uiX - X coordinate of point for conversion
     * @param uiY - Y coordinate of point for conversion
     * @param ret - result point
     */
    public void convertToGL(float uiX, float uiY, CCPoint ret) {
    	float newX = uiX / surfaceSize_.width * m_obWinSizeInPoints.width;
        float newY = m_obWinSizeInPoints.height - uiY / surfaceSize_.height * m_obWinSizeInPoints.height;
        
        switch (deviceOrientation_) {
            case kCCDeviceOrientationPortrait:
                ret.setPoint(newX, newY);
            	//ret.set(newY, newX);
                break;

            case kCCDeviceOrientationLandscapeLeft:
                // ret = CGPoint.ccp(uiPoint.y, uiPoint.x);
            	ret.setPoint(newX, newY);
                break;
        }

	    if (m_fContentScaleFactor != 1 && isContentScaleSupported_ )
		    CGPointUtil.mult(ret, m_fContentScaleFactor);
    }



    // Director CCScene Management












    /**
     * this should be called from activity when activity pause
     */
    public void onPause() {
    	openGLView_.onPause();
    	pause();
    }

    /**
     * this should be called from activity when activity resume
     */
    public void onResume() {
    	openGLView_.onResume();
    	resume();
    }
    






    public void setCCTexture2D(GL10 gl, boolean on) {
        if (on)
            gl.glEnable(GL_TEXTURE_2D);
        else
            gl.glDisable(GL_TEXTURE_2D);
    }

    public void updateContentScaleFactor() {
        /*
        // Based on code snippet from: http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html
        if (openGLView_.hasFunction(setContentScaleFactor)) {
            // XXX: To avoid compile warning when using Xcode 3.2.2
            typedef void (*CC_CONTENT_SCALE)(id, SEL, float);

            SEL selector = @selector(setContentScaleFactor:);
            CC_CONTENT_SCALE method = (CC_CONTENT_SCALE) [openGLView_ methodForSelector:selector];
            method(openGLView_,selector, contentScaleFactor_);

            // In Xcode 3.2.3 SDK 4.0, use this one:
            //		[openGLView_ setContentScaleFactor: scaleFactor];

            isContentScaleSupported_ = YES;

        } else {
            CCLOG(@"cocos2d: WARNING: calling setContentScaleFactor on iOS < 4. Using fallback mechanism");
            // on pre-4.0 iOS, use bounds/transform
            openGLView_.bounds = CGRectMake(0, 0,
                    openGLView_.bounds.size.width * contentScaleFactor_,
                    openGLView_.bounds.size.height * contentScaleFactor_);
            openGLView_.transform = CGAffineTransformScale(openGLView_.transform, 1 / contentScaleFactor_, 1 / contentScaleFactor_); 

            isContentScaleSupported_ = NO;
        }
        */
    }

    public int[] getConfigSpec() {
        if (mTranslucentBackground) {
            // We want a depth buffer and an getOpacity buffer
            int[] configSpec = {
                    EGL10.EGL_RED_SIZE, 8,
                    EGL10.EGL_GREEN_SIZE, 8,
                    EGL10.EGL_BLUE_SIZE, 8,
                    EGL10.EGL_ALPHA_SIZE, 8,
                    EGL10.EGL_DEPTH_SIZE, 16,
                    EGL10.EGL_NONE
            };
            return configSpec;
        } else {
            // We want a depth buffer, don't care about the
            // details of the color buffer.
            int[] configSpec = {
                    EGL10.EGL_DEPTH_SIZE, 16,
                    EGL10.EGL_NONE
            };
            return configSpec;
        }
    }

	private TextBuilder fpsBuilder = new TextBuilder();

	private void showFPS() {
		GL10 gl = CCDirector.gl;
		if (FAST_FPS_DISPLAY) {
			// display the FPS using a LabelAtlas
			// updates the FPS every frame

			m_uFrames++;
			m_fAccumDt += m_fDeltaTime;

			if (m_fAccumDt > ccConfig.CC_DIRECTOR_FPS_INTERVAL) {
				m_fFrameRate = m_uFrames / m_fAccumDt;
				m_uFrames = 0;
				m_fAccumDt = 0;

				m_pFPSLabel.setString(CCFormatter.format("%.1f", m_fFrameRate));
			}

			m_pFPSLabel.draw();
		} else {
			// display the FPS using a manually generated Texture (very slow)
			// updates the FPS 3 times per second aprox.

			m_uFrames++;
			m_fAccumDt += m_fDeltaTime;

			if (m_fAccumDt > ccConfig.CC_DIRECTOR_FPS_INTERVAL) {
				m_fFrameRate = m_uFrames / m_fAccumDt;
				m_uFrames = 0;
				m_fAccumDt = 0;
			}

			String str = CCFormatter.format("%.2f", m_fFrameRate);
			CCTexture2D texture = new CCTexture2D();
			// no need to register Loader for this subordinate texture
			texture.initWithText(str, CCSize.Make(100, 30), TextAlignment.LEFT, "DroidSans", 24);

			// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
			// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
			// Unneeded states: GL_COLOR_ARRAY

			gl.glDisableClientState(GL_COLOR_ARRAY);
			gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			gl.glColor4f(224.f / 255, 224.f / 255, 244.f / 255, 200.f / 255);
			texture.drawAtPoint(gl, CCPointExtension.ccp(5, 2));

			gl.glBlendFunc(ccConfig.CC_BLEND_SRC, ccConfig.CC_BLEND_DST);

			// restore default GL state
			gl.glEnableClientState(GL_COLOR_ARRAY);
		}
	}

    private boolean mTranslucentBackground = false;
    
    public boolean onKeyDown(KeyEvent event){
    	if (!CCKeyDispatcher.sharedDispatcher().getDispatchEvents())
    		return false;
    	CCKeyDispatcher.sharedDispatcher().queueMotionEvent(event);
    	return true;
    }
    
	//added by Ishaq 
	public boolean onKeyUp(KeyEvent event){
    	if (!CCKeyDispatcher.sharedDispatcher().getDispatchEvents())
    		return false;

    	CCKeyDispatcher.sharedDispatcher().queueMotionEvent(event);
    	return true;
    }
	
	//added by Ishaq 
	public void setIsEnableKeyEvent(boolean b){
		CCKeyDispatcher.sharedDispatcher().setDispatchEvents(b);
	}
	
	//added by Ishaq 
	public boolean isEnableKeyEvent(){
		return CCKeyDispatcher.sharedDispatcher().getDispatchEvents();
	}

	public static GL10 gl;
}

/** 
 * @brief DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display.
 * 
 * Features and Limitations:
 *  - Scheduled timers & drawing are synchronizes with the refresh rate of the display
 *  - Only supports animation intervals of 1/60 1/30 & 1/15
 * 
 * @since v0.8.2
 */
class CCDisplayLinkDirector extends CCDirector {
	public CCDisplayLinkDirector() {
		m_bInvalid = false;
	}

	@Override
	public void mainLoop() {
		if (m_bPurgeDirecotorInNextLoop) {
			m_bPurgeDirecotorInNextLoop = false;
			purgeDirector();
		} else if (! m_bInvalid) {
			drawScene();

			// release the objects
// TODO			CCPoolManager.sharedPoolManager().pop();
		}
	}

	@Override
	public void setAnimationInterval(double dValue) {
		m_dAnimationInterval = dValue;
		if (! m_bInvalid) {
			stopAnimation();
			startAnimation();
		}
	}

	@Override
	public void startAnimation() {
		m_pLastUpdate = System.currentTimeMillis();

		m_bInvalid = false;
		CCApplication.sharedApplication().setAnimationInterval(m_dAnimationInterval);
	}

	@Override
	public void stopAnimation() {
		m_bInvalid = true;
	}

	protected boolean m_bInvalid;
}

// end of base_node group
/// @}
