package org.cocos2d.nodes;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.microedition.khronos.opengles.GL10;

import org.cocos2d.CCDirector;
import org.cocos2d.actions.CCActionManager;
import org.cocos2d.actions.CCScheduler;
import org.cocos2d.actions.UpdateCallback;
import org.cocos2d.actions.base.CCAction;
import org.cocos2d.config.ccConfig;
import org.cocos2d.config.ccMacros;
import org.cocos2d.grid.CCGridBase;
import org.cocos2d.opengl.CCCamera;
import org.cocos2d.touch_dispatcher.CCTouch;
import org.cocos2d.types.CGAffineTransform;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.CGRect;
import org.cocos2d.types.CGSize;
import org.cocos2d.types.util.CGAffineTransformUtil;
import org.cocos2d.types.util.CGPointUtil;
import org.cocos2d.types.util.PoolHolder;
import org.cocos2d.utils.CCFormatter;
import org.cocos2d.utils.Util5;
import org.cocos2d.utils.javolution.MathLib;
import org.cocos2d.utils.pool.OneClassPool;

import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;

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

/** @brief CCNode is the main element. Anything that gets drawn or contains things that get drawn is a CCNode.
 * The most popular CCNodes are: CCScene, CCLayer, CCSprite, CCMenu.
 * 
 * The main features of a CCNode are:
 * - They can contain other CCNode nodes (addChild, getChildByTag, removeChild, etc)
 * - They can schedule periodic callback (schedule, unschedule, etc)
 * - They can execute actions (runAction, stopAction, etc)
 * 
 * Some CCNode nodes provide extra functionality for them or their children.
 * 
 * Subclassing a CCNode usually means (one/all) of:
 * - overriding init to initialize resources and schedule callbacks
 * - create callbacks to handle the advancement of time
 * - overriding draw to render the node
 * 
 * Features of CCNode:
 * - position
 * - scale (x, y)
 * - rotation (in degrees, clockwise)
 * - CCCamera (an interface to gluLookAt )
 * - CCGridBase (to do mesh transformations)
 * - anchor point
 * - size
 * - visible
 * - z-order
 * - openGL z position
 * 
 * Default values:
 * - rotation: 0
 * - position: (x=0,y=0)
 * - scale: (x=1,y=1)
 * - contentSize: (x=0,y=0)
 * - anchorPoint: (x=0,y=0)
 * 
 * Limitations:
 * - A CCNode is a "void" object. It doesn't have a texture
 * 
 * Order in transformations with grid disabled
 * -# The node will be translated (position)
 * -# The node will be rotated (rotation)
 * -# The node will be scaled (scale)
 * -# The node will be moved according to the camera values (camera)
 * 
 * Order in transformations with grid enabled
 * -# The node will be translated (position)
 * -# The node will be rotated (rotation)
 * -# The node will be scaled (scale)
 * -# The grid will capture the screen
 * -# The node will be moved according to the camera values (camera)
 * -# The grid will render the captured screen
 * 
 * Camera:
 * - Each node has a camera. By default it points to the center of the CCNode.
 */
public class CCNode {
	private static final String TAG = CCNode.class.getSimpleName();

	public static final int kCCNodeTagInvalid = -1;

	public static final int kCCNodeOnEnter = 0;
	public static final int kCCNodeOnExit = 1;
	public static final int kCCNodeOnEnterTransitionDidFinish = 2;
	public static final int kCCNodeOnExitTransitionDidStart = 3;
	public static final int kCCNodeOnCleanup = 4;

	/// @{
	/// @name Constructor, Distructor and Initializers

	/**
	 * Default constructor
	 */
	protected CCNode() {
		m_fRotationX = 0.0f;
		m_fRotationY = 0.0f;
		m_fScaleX = 1.0f;
		m_fScaleY = 1.0f;
		m_fVertexZ = 0.0f;
		m_obPosition = CGPoint.zero();
		m_fSkewX = 0.0f;
		m_fSkewY = 0.0f;
		m_obAnchorPointInPoints = CGPoint.zero();
		m_obAnchorPoint = CGPoint.zero();
		m_obContentSize = CGSize.zero();
		m_sAdditionalTransform = CGAffineTransform.identity();
		m_pCamera = null;
		// children (lazy allocs)
		// lazy alloc
		m_pGrid = null;
		m_nZOrder = 0;
		m_pChildren = null;
		m_pParent = null;
		// "whole screen" objects. like Scenes and Layers, should set m_bIgnoreAnchorPointForPosition to false
		m_nTag = kCCNodeTagInvalid;
		// userData is always inited as nil
		m_pUserObject = null;
// TODO		m_pShaderProgram = null;
// TODO		m_eGLServerState = ccGLServerState(0);
		m_uOrderOfArrival = 0;
		m_bRunning = false;
		m_bTransformDirty = true;
		m_bInverseDirty = true;
		m_bAdditionalTransformDirty = false;
		m_bVisible = true;
		m_bIgnoreAnchorPointForPosition = false;
		m_bReorderChildDirty = false;
		m_nScriptHandler = 0;
		m_nUpdateScriptHandler = 0;

		// set default scheduler and actionManager
		CCDirector director = CCDirector.sharedDirector();
		m_pActionManager = director.getActionManager();
		m_pScheduler = director.getScheduler();

// TODO		CCScriptEngineProtocol pEngine = CCScriptEngineManager.sharedManager().getScriptEngine();
// TODO		m_eScriptType = (pEngine != null) ? pEngine.getScriptType() : kScriptTypeNone;

		// TODO legacy -->

		transformGL_ = new float[16];

		m_sTransform = CGAffineTransform.identity();
		m_sInverse = CGAffineTransform.identity();

		// "whole screen" objects. like Scenes and Layers, should set relativeAnchorPoint to false        
		isRelativeAnchorPoint_ = true;

		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
	}

	/**
	 *  Initializes the instance of CCNode
	 *  @return Whether the initialization was successful.
	 */
	public boolean init() {
		return true;
	}

	/**
	 * Allocates and initializes a node.
	 * @return A initialized node which is marked as "autorelease".
	 */
	public static CCNode create() {
		CCNode pRet = new CCNode();
		if(! pRet.init()) {
			pRet = null;
		}
		return pRet;
	}

	/**
	 * Gets the description string. It makes debugging easier.
	 * @return A string terminated with '\0'
	 */
	public String description() {
		return CCFormatter.format("<CCNode | Tag = %d>", m_nTag);
	}

	/// @} end of initializers

	/// @{
	/// @name Setters & Getters for Graphic Peroperties

	/**
	 * Sets the Z order which stands for the drawing order, and reorder this node in its parent's children array.
	 *
	 * The Z order of node is relative to its "brothers": children of the same parent.
	 * It's nothing to do with OpenGL's z vertex. This one only affects the draw order of nodes in cocos2d.
	 * The larger number it is, the later this node will be drawn in each message loop.
	 * Please refer to setVertexZ(float) for the difference.
	 *
	 * @param nZOrder   Z order of this node.
	 */
	public void setZOrder(int zOrder) {
		_setZOrder(zOrder);
		if(m_pParent != null) {
			m_pParent.reorderChild(this, zOrder);
		}
	}

	/**
	 * Sets the z order which stands for the drawing order
	 *
	 * This is an internal method. Don't call it outside the framework.
	 * The difference between setZOrder(int) and _setOrder(int) is:
	 * - _setZOrder(int) is a pure setter for m_nZOrder memeber variable
	 * - setZOrder(int) firstly changes m_nZOrder, then recorder this node in its parent's chilren array.
	 */
	public void _setZOrder(int z) {
		m_nZOrder = z;
	}

	/** The z order of the node relative to it's "brothers": children of the same parent */
	public int getZOrder() {
		return m_nZOrder;
	}

	/**
	 * Sets the real OpenGL Z vertex.
	 *
	 * Differences between openGL Z vertex and cocos2d Z order:
	 * - OpenGL Z modifies the Z vertex, and not the Z order in the relation between parent-children
	 * - OpenGL Z might require to set 2D projection
	 * - cocos2d Z order works OK if all the nodes uses the same openGL Z vertex. eg: vertexZ = 0
	 *
	 * @warning Use it at your own risk since it might break the cocos2d parent-children z order
	 *
	 * @param fVertexZ  OpenGL Z vertex of this node.
	 */
	public void setVertexZ(float fVertexZ) {
		m_fVertexZ = fVertexZ;
	}

	/**
	 * Gets OpenGL Z vertex of this node.
	 *
	 * @see setVertexZ(float)
	 *
	 * @return OpenGL Z vertex of this node
	 */
	public float getVertexZ() {
		return m_fVertexZ;
	}

	/**
	 * Changes the scale factor on X axis of this node
	 *
	 * The deafult value is 1.0 if you haven't changed it before
	 *
	 * @param scaleX   The scale factor on X axis.
	 */
	public void setScaleX(float scaleX) {
		m_fScaleX = scaleX;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Returns the scale factor on X axis of this node
	 *
	 * @see setScaleX(float)
	 *
	 * @return The scale factor on X axis.
	 */
	public float getScaleX() {
		return m_fScaleX;
	}

	/**
	 * Changes the scale factor on Y axis of this node
	 *
	 * The Default value is 1.0 if you haven't changed it before.
	 *
	 * @param scaleY   The scale factor on Y axis.
	 */
	public void setScaleY(float scaleY) {
		m_fScaleY = scaleY;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Returns the scale factor on Y axis of this node
	 *
	 * @see setScaleY(float)
	 *
	 * @return The scale factor on Y axis. 
	 */
	public float getScaleY() {
		return m_fScaleY;
	}

	/**
	 * Changes both X and Y scale factor of the node.
	 *
	 * 1.0 is the default scale factor. It modifies the X and Y scale at the same time.
	 *
	 * @param scale     The scale factor for both X and Y axis.
	 */
	public void setScale(float scale) {
		m_fScaleX = m_fScaleY = scale;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}
	/**
	 * Gets the scale factor of the node,  when X and Y have the same scale factor.
	 *
	 * @warning Assert when m_fScaleX != m_fScaleY.
	 * @see setScale(float)
	 *
	 * @return The scale factor of the node.
	 */
	public float getScale() {
		assert m_fScaleX == m_fScaleY : "CCNode#scale. ScaleX != ScaleY. Don't know which one to return";
		return m_fScaleX;
	}

	/**
	 * Changes the position (x,y) of the node in OpenGL coordinates
	 *
	 * Usually we use ccp(x,y) to compose CCPoint object.
	 * The original point (0,0) is at the left-bottom corner of screen.
	 * For example, this codesnip sets the node in the center of screen.
	 * @code
	 * CCSize size = CCDirector::sharedDirector()->getWinSize();
	 * node->setPosition( ccp(size.width/2, size.height/2) )
	 * @endcode
	 *
	 * @param position  The position (x,y) of the node in OpenGL coordinates
	 */
	public void setPosition(CGPoint position) {
		m_obPosition = position;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Gets the position (x,y) of the node in OpenGL coordinates
	 * 
	 * @see setPosition(const CCPoint&)
	 *
	 * @return The position (x,y) of the node in OpenGL coordinates
	 */
	public CGPoint getPosition() {
		return m_obPosition;
	}

	/**
	 * Sets position in a more efficient way.
	 *
	 * Passing two numbers (x,y) is much efficient than passing CCPoint object.
	 * This method is binded to lua and javascript. 
	 * Passing a number is 10 times faster than passing a object from lua to c++
	 *
	 * @code
	 * // sample code in lua
	 * local pos  = node::getPosition()  -- returns CCPoint object from C++
	 * node:setPosition(x, y)            -- pass x, y coordinate to C++
	 * @endcode
	 *
	 * @param x     X coordinate for position
	 * @param y     Y coordinate for position
	 */
	public void setPosition(float x, float y) {
		setPosition(CGPoint.ccp(x, y));
	}

	/**
	 * Gets position in a more efficient way, returns two number instead of a CCPoint object
	 *
	 * @see setPosition(float, float)
	 */
	public void getPosition(Float x, Float y) {
		x = m_obPosition.x;
		y = m_obPosition.y;
	}

	/**
	 * Gets/Sets x or y coordinate individually for position.
	 * These methods are used in Lua and Javascript Bindings
	 */
	public void setPositionX(float x) {
		setPosition(CGPoint.ccp(x, m_obPosition.y));
	}

	public float getPositionX() {
		return m_obPosition.x;
	}

	public void setPositionY(float y) {
		setPosition(CGPoint.ccp(m_obPosition.x, y));
	}

	public float getPositionY() {
		return m_obPosition.y;
	}

	/**
	 * Changes the X skew angle of the node in degrees.
	 *
	 * This angle describes the shear distortion in the X direction.
	 * Thus, it is the angle between the Y axis and the left edge of the shape
	 * The default skewX angle is 0. Positive values distort the node in a CW direction.
	 *
	 * @param newSkewX The X skew angle of the node in degrees.
	 */
	public void setSkewX(float newSkewX) {
		m_fSkewX = newSkewX;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Returns the X skew angle of the node in degrees.
	 *
	 * @see setSkewX(float)
	 *
	 * @return The X skew angle of the node in degrees.
	 */
	public float getSkewX() {
		return m_fSkewX;
	}

	/**
	 * Changes the Y skew angle of the node in degrees.
	 *
	 * This angle describes the shear distortion in the Y direction.
	 * Thus, it is the angle between the X axis and the bottom edge of the shape
	 * The default skewY angle is 0. Positive values distort the node in a CCW direction.
	 *
	 * @param fSkewY    The Y skew angle of the node in degrees.
	 */
	public void setSkewY(float newSkewY) {
		m_fSkewY = newSkewY;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Returns the Y skew angle of the node in degrees.
	 *
	 * @see setSkewY(float)
	 *
	 * @return The Y skew angle of the node in degrees.
	 */
	public float getSkewY() {
		return m_fSkewY;
	}

	/**
	 * Sets the anchor point in percent.
	 *
	 * anchorPoint is the point around which all transformations and positioning manipulations take place.
	 * It's like a pin in the node where it is "attached" to its parent.
	 * The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner.
	 * But you can use values higher than (1,1) and lower than (0,0) too.
	 * The default anchorPoint is (0.5,0.5), so it starts in the center of the node.
	 *
	 * @param anchorPoint   The anchor point of node.
	 */
	public void setAnchorPoint(CGPoint anchorPoint) {
		if(! CGPoint.equalToPoint(anchorPoint, m_obAnchorPoint)) {
			m_obAnchorPoint = anchorPoint;
			m_obAnchorPointInPoints = CGPoint.ccp(	m_obContentSize.width * m_obAnchorPoint.x, 
													m_obContentSize.height * m_obAnchorPoint.y);
			m_bTransformDirty = m_bInverseDirty = true;

			// TODO legacy -->
			if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
				isTransformGLDirty_ = true;
			}
			// TODO legacy <--
		}
	}

	/** 
	 * Returns the anchor point in percent.
	 *
	 * @see setAnchorPoint(const CCPoint&)
	 *
	 * @return The anchor point of node.
	 */
	public CGPoint getAnchorPoint() {
		return m_obAnchorPoint;
	}

	/**
	 * Returns the anchorPoint in absolute pixels.
	 * 
	 * @warning You can only read it. If you wish to modify it, use anchorPoint instead.
	 * @see getAnchorPoint()
	 *
	 * @return The anchor point in absolute pixels.
	 */
	public CGPoint getAnchorPointInPoints() {
		return m_obAnchorPointInPoints;
	}

	/**
	 * Sets the untransformed size of the node.
	 *
	 * The contentSize remains the same no matter the node is scaled or rotated.
	 * All nodes has a size. Layer and Scene has the same size of the screen.
	 *
	 * @param contentSize   The untransformed size of the node.
	 */
	public void setContentSize(CGSize contentSize) {
		if(! CGSize.equalToSize(contentSize, m_obContentSize)) {
			m_obContentSize = contentSize;

			m_obAnchorPointInPoints = CGPoint.ccp(	m_obContentSize.width * m_obAnchorPoint.x,
												m_obContentSize.height * m_obAnchorPoint.y);
			m_bTransformDirty = m_bInverseDirty = true;

			// TODO legacy -->
			if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
				isTransformGLDirty_ = true;
			}
			// TODO legacy <--
		}
	}

	/**
	 * Returns the untransformed size of the node.
	 *
	 * @see setContentSize(const CCSize&)
	 *
	 * @return The untransformed size of the node.
	 */
	public CGSize getContentSize() {
		return m_obContentSize;
	}

	/**
	 * Sets whether the node is visible
	 *
	 * The default value is true, a node is default to visible
	 *
	 * @param visible   true if the node is visible, false if the node is hidden.
	 */
	public void setVisible(boolean visible) {
		this.m_bVisible = visible;
	}

	/**
	 * Determines if the node is visible
	 *
	 * @see setVisible(bool)
	 *
	 * @return true if the node is visible, false if the node is hidden.
	 */
	public boolean isVisible() {
		return m_bVisible;
	}

	/** 
	 * Sets the rotation (angle) of the node in degrees. 
	 * 
	 * 0 is the default rotation angle. 
	 * Positive values rotate node clockwise, and negative values for anti-clockwise.
	 * 
	 * @param rotation     The roration of the node in degrees.
	 */
	public void setRotation(float rotation) {
		m_fRotationX = m_fRotationY = rotation;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Returns the rotation of the node in degrees.
	 *
	 * @see setRotation(float)
	 *
	 * @return The rotation of the node in degrees.
	 */
	public float getRotation() {
		assert m_fRotationX == m_fRotationY : "CCNode#rotation. RotationX != RotationY. Don't know which one to return";
		return m_fRotationX;
	}

	/** 
	 * Sets the X rotation (angle) of the node in degrees which performs a horizontal rotational skew.
	 * 
	 * 0 is the default rotation angle. 
	 * Positive values rotate node clockwise, and negative values for anti-clockwise.
	 * 
	 * @param fRotationX    The X rotation in degrees which performs a horizontal rotational skew.
	 */
	public void setRotationX(float rotaionX) {
		m_fRotationX = rotaionX;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Gets the X rotation (angle) of the node in degrees which performs a horizontal rotation skew.
	 *
	 * @see setRotationX(float)
	 *
	 * @return The X rotation in degrees.
	 */
	public float getRotationX() {
		return m_fRotationX;
	}

	/**
	 * Sets the Y rotation (angle) of the node in degrees which performs a vertical rotational skew.
	 * 
	 * 0 is the default rotation angle. 
	 * Positive values rotate node clockwise, and negative values for anti-clockwise.
	 *
	 * @param rotationY    The Y rotation in degrees.
	 */
	public void setRotationY(float rotationY) {
		m_fRotationY = rotationY;
		m_bTransformDirty = m_bInverseDirty = true;

		// TODO legacy -->
		if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
			isTransformGLDirty_ = true;
		}
		// TODO legacy <--
	}

	/**
	 * Gets the Y rotation (angle) of the node in degrees which performs a vertical rotational skew.
	 *
	 * @see setRotationY(float)
	 *
	 * @return The Y rotation in degrees.
	 */
	public float getRotationY() {
		return m_fRotationX;
	}

	/**
	 * Sets the arrival order when this node has a same ZOrder with other children.
	 *
	 * A node which called addChild subsequently will take a larger arrival order,
	 * If two children have the same Z order, the child with larger arrival order will be drawn later.
	 *
	 * @warning This method is used internally for zOrder sorting, don't change this manually
	 *
	 * @param uOrderOfArrival   The arrival order.
	 */
	public void setOrderOfArrival(int orderOfArrival) {
		m_uOrderOfArrival = orderOfArrival;
	}

	/**
	 * Returns the arrival order, indecates which children is added previously.
	 *
	 * @see setOrderOfArrival(unsigned int)
	 *
	 * @return The arrival order.
	 */
	public int getOrderOfArrival() {
		return m_uOrderOfArrival;
	}

	/**
	 * Sets whether the anchor point will be (0,0) when you position this node.
	 *
	 * This is an internal method, only used by CCLayer and CCScene. Don't call it outside framework.
	 * The default value is false, while in CCLayer and CCScene are true
	 *
	 * @param ignore    true if anchor point will be (0,0) when you position this node
	 * @todo This method shoud be renamed as setIgnoreAnchorPointForPosition(bool) or something with "set"
	 */
	public void ignoreAnchorPointForPosition(boolean ignore) {
		if (ignore != m_bIgnoreAnchorPointForPosition) {
			m_bIgnoreAnchorPointForPosition = ignore;
			m_bTransformDirty = m_bInverseDirty = true;

			// TODO legacy -->
			if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
				isTransformGLDirty_ = true;
			}
			// TODO legacy <--
		}
	}

	/**
	 * Gets whether the anchor point will be (0,0) when you position this node.
	 *
	 * @see ignoreAnchorPointForPosition(bool)
	 *
	 * @return true if the anchor point will be (0,0) when you position this node.
	 */
	public boolean isIgnoreAnchorPointForPosition() {
		return m_bIgnoreAnchorPointForPosition;
	}

	/// @}  end of Setters & Getters for Graphic Peroperties

	/// @{
	/// @name Children and Parent

	/** 
	 * Adds a child to the container with z-order as 0.
	 *
	 * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
	 *
	 * @param child A child node
	 */
	public void addChild(CCNode child) {
		assert child != null: "Argument must be non-nil" ;
		addChild(child, child.m_nZOrder, child.m_nTag);
	}

	/** 
	 * Adds a child to the container with a z-order
	 *
	 * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
	 *
	 * @param child     A child node
	 * @param zOrder    Z order for drawing priority. Please refer to setZOrder(int)
	 */
	public void addChild(CCNode child, int zOrder) {
		assert child != null: "Argument must be non-nil" ;
		addChild(child, zOrder, child.m_nTag);
	}

	/** 
	 * Adds a child to the container with z order and tag
	 *
	 * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
	 *
	 * @param child     A child node
	 * @param zOrder    Z order for drawing priority. Please refer to setZOrder(int)
	 * @param tag       A interger to identify the node easily. Please refer to setTag(int)
	 */
	public void addChild(CCNode child, int zOrder, int tag) {
		assert child != null : "Argument must be non-nil";
		assert child.m_pParent == null: "child already added. It can't be added again";

		if(m_pChildren == null) {
			this.childrenAlloc();
		}

		this.insertChild(child, zOrder);

		child.m_nTag = tag;

		child.setParent(this);
		child.setOrderOfArrival(s_globalOrderOfArrival++);

		if (m_bRunning) {
			child.onEnter();
			child.onEnterTransitionDidFinish();
		}
	}

	/**
	 * Gets a child from the container with its tag
	 *
	 * @param tag   An identifier to find the child node.
	 *
	 * @return a CCNode object whose tag equals to the input parameter
	 */
	public CCNode getChildByTag(int tag) {
		assert tag != kCCNodeTagInvalid : "Invalid tag";

		if (m_pChildren != null) {
			for(CCNode child : m_pChildren) {
				if (child.m_nTag == tag) {
					return child;
				}
			}
		}
		return null;
	}

	/**
	 * Return an array of children
	 *
	 * Composing a "tree" structure is a very important feature of CCNode
	 * Here's a sample code of traversing children array:
	 * @code
	 * CCNode* node = NULL;
	 * CCARRAY_FOREACH(parent->getChildren(), node)
	 * {
	 *     node->setPosition(0,0);
	 * }
	 * @endcode
	 * This sample code traverses all children nodes, and set theie position to (0,0)
	 *
	 * @return An array of children
	 */
	public List<CCNode> getChildren() {
		return m_pChildren;
	}

	/** 
	 * Get the amount of children.
	 *
	 * @return The amount of children.
	 */
	public int getChildrenCount() {
		return (m_pChildren != null) ? m_pChildren.size() : 0;
	}

	/**
	 * Sets the parent node
	 *
	 * @param parent    A pointer to the parnet node
	 */
	public void setParent(CCNode parent) {
		m_pParent = parent;
	}

	/**
	 * Returns a pointer to the parent node
	 * 
	 * @see setParent(CCNode*)
	 *
	 * @returns A pointer to the parnet node
	 */
	public CCNode getParent() {
		return m_pParent;
	}

	////// REMOVES //////

	/** 
	 * Removes this node itself from its parent node with a cleanup.
	 * If the node orphan, then nothing happens.
	 * @see removeFromParentAndCleanup(bool)
	 */
	public void removeFromParent() {
		this.removeFromParentAndCleanup(true);
	}

	/** 
	 * Removes this node itself from its parent node. 
	 * If the node orphan, then nothing happens.
	 * @param cleanup   true if all actions and callbacks on this node should be removed, false otherwise.
	 */
	public void removeFromParentAndCleanup(boolean cleanup) {
		if(m_pParent != null) {
			m_pParent.removeChild(this, cleanup);
		}
	}

	/** 
	 * Removes a child from the container with a cleanup
	 *
	 * @see removeChild(CCNode, bool)
	 *
	 * @param child     The child node which will be removed.
	 */
	public void removeChild(CCNode child) {
		this.removeChild(child, true);
	}

	/** 
	 * Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.
	 * 
	 * @param child     The child node which will be removed.
	 * @param cleanup   true if all running actions and callbacks on the child node will be cleanup, false otherwise.
	 */
	public void removeChild(CCNode child, boolean cleanup) {
		// explicit nil handling
		if(child == null) {
			return;
		}

		if(m_pChildren.contains(child)) {
			this.detachChild(child, cleanup);
		}
	}

	/** 
	 * Removes a child from the container by tag value with a cleanup.
	 *
	 * @see removeChildByTag(int, bool)
	 *
	 * @param tag       An interger number that identifies a child node
	 */
	public void removeChildByTag(int tag) {
		this.removeChildByTag(tag, true);
	}

	/** 
	 * Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter
	 * 
	 * @param tag       An interger number that identifies a child node
	 * @param cleanup   true if all running actions and callbacks on the child node will be cleanup, false otherwise. 
	 */
	public void removeChildByTag(int tag, boolean cleanup) {
		assert tag != kCCNodeTagInvalid : "Invalid tag";

		CCNode child = this.getChildByTag(tag);

		if(child == null) {
			ccMacros.CCLOG(TAG, "cocos2d: removeChildByTag: child not found!");
		} else {
			this.removeChild(child, cleanup);
		}
	}

	/** 
	 * Removes all children from the container with a cleanup.
	 *
	 * @see removeAllChildrenWithCleanup(bool)
	 */
	public void removeAllChildren() {
		removeAllChildrenWithCleanup(true);
	}

	/** 
	 * Removes all children from the container, and do a cleanup to all running actions depending on the cleanup parameter.
	 *
	 * @param cleanup   true if all running actions on all children nodes should be cleanup, false oterwise.
	 */
	public void removeAllChildrenWithCleanup(boolean cleanup) {
		// not using detachChild improves speed here
		if (m_pChildren != null) {
			for(CCNode child : m_pChildren) {
				// IMPORTANT:
				//  -1st do onExit
				//  -2nd cleanup
				if(m_bRunning) {
					child.onExitTransitionDidStart();
					child.onExit();
				}
	
				if(cleanup) {
					child.cleanup();
				}
				// set parent nil at the end
				child.setParent(null);
			}

			m_pChildren.clear();
		}
	}

	/** 
	 * Reorders a child according to a new z value.
	 *
	 * @param child     An already added child node. It MUST be already added.
	 * @param zOrder    Z order for drawing priority. Please refer to setZOrder(int)
	 */
	public void reorderChild(CCNode child, int zOrder) {
		assert child != null : "Child must be non-null";
		m_bReorderChildDirty = true;
		child.setOrderOfArrival(s_globalOrderOfArrival++);
		child._setZOrder(zOrder);
	}

	/** 
	 * Sorts the children array once before drawing, instead of every time when a child is added or reordered.
	 * This appraoch can improves the performance massively.
	 * @note Don't call this manually unless a child added needs to be removed in the same frame 
	 */
	public void sortAllChildren() {
		if(m_bReorderChildDirty) {
			Collections.sort(m_pChildren, zOrderComparator);

			//don't need to check children recursively, that's done in visit of each child

			m_bReorderChildDirty = false;
		}
	}

	/// @} end of Children and Parent

	/// @{
	/// @name Grid object for effects

	/**
	 * Returns a grid object that is used when applying effects
	 * 
	 * @return A CCGrid object that is used when applying effects
	 */
	public CCGridBase getGrid() {
		return m_pGrid;
	}

	/**
	 * Changes a grid object that is used when applying effects
	 *
	 * @param A CCGrid object that is used when applying effects
	 */
	public void setGrid(CCGridBase grid) {
		this.m_pGrid = grid;
	}

	/// @} end of Grid

	/// @{
	/// @name Tag & User data

	/**
	 * Returns a tag that is used to identify the node easily.
	 *
	 * You can set tags to node then identify them easily.
	 * @code
	 * #define TAG_PLAYER  1
	 * #define TAG_MONSTER 2
	 * #define TAG_BOSS    3
	 * // set tags
	 * node1->setTag(TAG_PLAYER);
	 * node2->setTag(TAG_MONSTER);
	 * node3->setTag(TAG_BOSS);
	 * parent->addChild(node1);
	 * parent->addChild(node2);
	 * parent->addChild(node3);
	 * // identify by tags
	 * CCNode* node = NULL;
	 * CCARRAY_FOREACH(parent->getChildren(), node)
	 * {
	 *     switch(node->getTag())
	 *     {
	 *         case TAG_PLAYER:
	 *             break;
	 *         case TAG_MONSTER:
	 *             break;
	 *         case TAG_BOSS:
	 *             break;
	 *     }
	 * }
	 * @endcode
	 *
	 * @return A interger that identifies the node.
	 */
	public int getTag() {
		return m_nTag;
	}

	/**
	 * Changes the tag that is used to identify the node easily.
	 *
	 * Please refer to getTag for the sample code.
	 *
	 * @param A interger that indentifies the node.
	 */
	public void setTag(int tag) {
		m_nTag = tag;
	}

	/** 
	 * Returns a user assigned Object
	 * 
	 * Similar to userData, but instead of holding a void* it holds an object
	 *
	 * @return A user assigned Object
	 */
	public Object getUserObject() {
		return m_pUserObject;
	}

	/**
	 * Returns a user assigned Object
	 *
	 * Similar to UserData, but instead of holding a void* it holds an object.
	 * The UserObject will be retained once in this method,
	 * and the previous UserObject (if existed) will be relese.
	 * The UserObject will be released in CCNode's destructure.
	 *
	 * @param A user assigned Object
	 */
	public void setUserObject(Object userObject) {
		m_pUserObject = userObject;
	}

	/// @} end of Tag & User Data

	/// @{
	/// @name Shader Program

	/**
	 * Return the shader program currently used for this node
	 * 
	 * @return The shader program currelty used for this node
	 */
/* TODO
	public CCGLProgram getShaderProgram() {
		return m_pShaderProgram;
	}
*/
	/**
	 * Sets the shader program for this node
	 *
	 * Since v2.0, each rendering node must set its shader program.
	 * It should be set in initialize phase.
	 * @code
	 * node->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
	 * @endcode
	 * 
	 * @param The shader program which fetchs from CCShaderCache.
	 */
/* TODO
	public void setShaderProgram(CCGLProgram shaderProgram) {
		m_pShaderProgram = pShaderProgram;
	}
*/
	/// @} end of Shader Program

	/**
	 * Returns a camera object that lets you move the node using a gluLookAt
	 *
	 * @code
	 * CCCamera* camera = node->getCamera();
	 * camera->setEyeXYZ(0, 0, 415/2);
	 * camera->setCenterXYZ(0, 0, 0);
	 * @endcode
	 *
	 * @return A CCCamera object that lets you move the node using a gluLookAt
	 */
	public CCCamera getCamera() {
		if(m_pCamera == null) {
			m_pCamera = new CCCamera();
		}

		return m_pCamera;
	}

	/** 
	 * Returns whether or not the node accepts event callbacks.
	 * 
	 * Running means the node accept event callbacks like onEnter(), onExit(), update()
	 *
	 * @return Whether or not the node is running.
	 */
	public boolean isRunning() {
		return m_bRunning;
	}

	/// @{
	/// @name Script Bindings for lua

	/**
	 * Registers a script function that will be called in onEnter() & onExit() seires functions.
	 * 
	 * This handler will be removed automatically after onExit() called.
	 * @code
	 * -- lua sample
	 * local function sceneEventHandler(eventType)
	 *     if eventType == kCCNodeOnEnter then
	 *         -- do something
	 *     elseif evetType == kCCNodeOnExit then
	 *         -- do something
	 *     end
	 * end
	 * scene::registerScriptHandler(sceneEventHandler)
	 * @endcode
	 *
	 * @warning This method is for internal usage, don't call it manually.
	 * @todo Perhaps we should rename it to get/set/removeScriptHandler acoording to the function name style.
	 *
	 * @param handler   A number that indicates a lua function. 
	 */
	public void registerScriptHandler(int handler) {
		unregisterScriptHandler();
		m_nScriptHandler = handler;
// TODO		LUALOG("[LUA] Add CCNode event handler: %d", m_nScriptHandler);
	}

	/**
	 * Unregisters a script function that will be called in onEnter() & onExit() series functions.
	 *
	 * @see registerScriptHandler(int)
	 */
	public void unregisterScriptHandler() {
		if(m_nScriptHandler != 0) {
// TODO			CCScriptEngineManager.sharedManager().getScriptEngine().removeScriptHandler(m_nScriptHandler);
// TODO			LUALOG("[LUA] Remove CCNode event handler: %d", m_nScriptHandler);
			m_nScriptHandler = 0;
		}
	}

	/**
	 * Gets script handler for onEnter/onExit event.
	 * This is an internal method. g
	 * @see registerScriptHandler(int)
	 *
	 * @return A number that indicates a lua function.
	 */
	public int getScriptHandler() {
		// TODO		return m_nScriptHandler;
		return 0;
	};

	/** 
	 * Schedules for lua script. 
	 */
	public void scheduleUpdateWithPriorityLua(int nHandler, int priority) {
		unscheduleUpdate();
		m_nUpdateScriptHandler = nHandler;
// TODO		m_pScheduler.scheduleUpdateForTarget(this, priority, !m_bRunning);
	}

	/// @}  end Script Bindings

	/// @{
	/// @name Event Callbacks

	/** 
	 * Event callback that is invoked every time when CCNode enters the 'stage'.
	 * If the CCNode enters the 'stage' with a transition, this event is called when the transition starts.
	 * During onEnter you can't access a "sister/brother" node.
	 * If you override onEnter, you shall call its parent's one, e.g., CCNode::onEnter().
	 */
	public void onEnter() {
		if(m_pChildren != null) {
			for (CCNode child: m_pChildren) {
				child.onEnter();
			}
		}

		this.resumeSchedulerAndActions();

		m_bRunning = true;
/* TODO
		if(m_eScriptType != kScriptTypeNone) {
			CCScriptEngineManager.sharedManager().getScriptEngine().executeNodeEvent(this, kCCNodeOnEnter);
		}
*/
	}

	/** Event callback that is invoked when the CCNode enters in the 'stage'.
	 * If the CCNode enters the 'stage' with a transition, this event is called when the transition finishes.
	 * If you override onEnterTransitionDidFinish, you shall call its parent's one, e.g. CCNode::onEnterTransitionDidFinish()
	 */
	public void onEnterTransitionDidFinish() {
		if (m_pChildren != null) {
			for (CCNode child: m_pChildren) {
				child.onEnterTransitionDidFinish();
			}
		}
/* TODO
		if(m_eScriptType == kScriptTypeJavascript) {
			CCScriptEngineManager.sharedManager().getScriptEngine().executeNodeEvent(this, kCCNodeOnEnterTransitionDidFinish);
		}
*/
	}

	/** 
	 * Event callback that is invoked every time the CCNode leaves the 'stage'.
	 * If the CCNode leaves the 'stage' with a transition, this event is called when the transition finishes.
	 * During onExit you can't access a sibling node.
	 * If you override onExit, you shall call its parent's one, e.g., CCNode::onExit().
	 */
	public void onExit() {
		this.pauseSchedulerAndActions();

		m_bRunning = false;
/* TODO
		if(m_eScriptType != kScriptTypeNone) {
			CCScriptEngineManager.sharedManager().getScriptEngine().executeNodeEvent(this, kCCNodeOnExit);
		}
*/
		if(m_pChildren != null) {
			for(CCNode child : m_pChildren) {
				child.onExit();
			}
		}
	}

	/** 
	 * Event callback that is called every time the CCNode leaves the 'stage'.
	 * If the CCNode leaves the 'stage' with a transition, this callback is called when the transition starts.
	 */
	public void onExitTransitionDidStart() {
		if(m_pChildren != null) {
			for(CCNode child : m_pChildren) {
				child.onExitTransitionDidStart();
			}
		}
/* TODO
		if(m_eScriptType == kScriptTypeJavascript) {
			CCScriptEngineManager.sharedManager().getScriptEngine().executeNodeEvent(this, kCCNodeOnExitTransitionDidStart);
		}
*/
	}

	/// @} end of event callbacks.

	/** 
	 * Stops all running actions and schedulers
	 */
	public void cleanup() {
		// actions
		this.stopAllActions();
		this.unscheduleAllSelectors();

		// timers
		if (m_pChildren != null) {
			for(CCNode child : m_pChildren) {
				child.cleanup();
			}
		}
	}

	/** 
	 * Override this method to draw your own node.
	 * The following GL states will be enabled by default:
	 * - glEnableClientState(GL_VERTEX_ARRAY);
	 * - glEnableClientState(GL_COLOR_ARRAY);
	 * - glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	 * - glEnable(GL_TEXTURE_2D);
	 * AND YOU SHOULD NOT DISABLE THEM AFTER DRAWING YOUR NODE
	 * But if you enable any other GL state, you should disable it after drawing your node.
	 */
	public void draw(GL10 gl) {
		// override me
		// Only use this function to draw your staff.
		// DON'T draw your stuff outside this method
	}

	/** 
	 * Visits this node's children and draw them recursively.
	 */
	public void visit(GL10 gl) {
		// quick return if not visible. children won't be drawn.
		if (! m_bVisible) {
			return;
		}
		gl.glPushMatrix();

		if ((m_pGrid != null) && m_pGrid.isActive()) {
			m_pGrid.beforeDraw(gl);
			transformAncestors(gl);
		}

		this.transform(gl);

		if (m_pChildren != null) {
			sortAllChildren();
			// draw children zOrder < 0
			for (int i=0; i<m_pChildren.size(); ++i) {
				CCNode child = m_pChildren.get(i);
				if (child.m_nZOrder < 0) {
					child.visit(gl);
				} else
					break;
			}
		}

		// self draw
		this.draw(gl);

		if (m_pChildren != null) {
			for (int i=0; i<m_pChildren.size(); ++i) {
				CCNode child = m_pChildren.get(i);
				if (child.m_nZOrder >= 0) {
					child.visit(gl);
				}
			}
		}

		// reset for next frame
		m_uOrderOfArrival = 0;

		if ((m_pGrid != null) && m_pGrid.isActive()) {
			m_pGrid.afterDraw(gl, this);
		}

		gl.glPopMatrix();
	}

	/** 
	 * Returns a "local" axis aligned bounding box of the node.
	 * The returned box is relative only to its parent.
	 *
	 * @note This method returns a temporaty variable, so it can't returns const CCRect&
	 * @todo Rename to getBoundingBox() in the future versions.
	 * 
	 * @return A "local" axis aligned boudning box of the node.
	 */
	public CGRect boundingBox() {
		CGRect rect = CGRect.make(0, 0, m_obContentSize.width, m_obContentSize.height);
		return CGRect.applyAffineTransform(rect, nodeToParentTransform());
	}

	/// @{
	/// @name Actions

	/**
	 * Sets the CCActionManager object that is used by all actions.
	 *
	 * @warning If you set a new CCActionManager, then previously created actions will be removed.
	 *
	 * @param actionManager     A CCActionManager object that is used by all actions.
	 */
	public void setActionManager(CCActionManager actionManager) {
		if(actionManager != m_pActionManager) {
			this.stopAllActions();
			m_pActionManager = actionManager;
		}
	}

	/**
	 * Gets the CCActionManager object that is used by all actions.
	 * @see setActionManager(CCActionManager*)
	 * @return A CCActionManager object.
	 */
	public CCActionManager getActionManager() {
		return m_pActionManager;
	}

	/** 
	 * Executes an action, and returns the action that is executed.
	 *
	 * This node becomes the action's target. Refer to CCAction::getTarget()
	 * @warning Actions don't retain their target.
	 *
	 * @return An Action pointer
	 */
	public CCAction runAction(CCAction action) {
		assert action != null : "Argument must be non-null";
		CCActionManager.sharedManager().addAction(action, this, !m_bRunning);
		return action;
	}

	/** 
	 * Stops and removes all actions from the running action list .
	 */
	public void stopAllActions() {
		CCActionManager.sharedManager().removeAllActions(this);
	}

	/** 
	 * Stops and removes an action from the running action list.
	 *
	 * @param An action object to be removed.
	 */
	public void stopAction(CCAction action) {
		CCActionManager.sharedManager().removeAction(action);
	}

	/** 
	 * Removes an action from the running action list by its tag.
	 *
	 * @param A tag that indicates the action to be removed.
	 */
	public void stopActionByTag(int tag) {
		assert tag != CCAction.kCCActionTagInvalid : "Invalid tag";
		CCActionManager.sharedManager().removeAction(tag, this);
	}

	/** 
	 * Gets an action from the running action list by its tag.
	 *
	 * @see setTag(int), getTag().
	 *
	 * @return The action object with the given tag.
	 */
	public CCAction getActionByTag(int tag) {
		assert tag != CCAction.kCCActionTagInvalid : "Invalid tag";
		return CCActionManager.sharedManager().getAction(tag, this);
	}

	/** 
	 * Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays).
	 *
	 * Composable actions are counted as 1 action. Example:
	 *    If you are running 1 Sequence of 7 actions, it will return 1.
	 *    If you are running 7 Sequences of 2 actions, it will return 7.
	 * @todo Rename to getNumberOfRunningActions()
	 *
	 * @return The number of actions that are running plus the ones that are schedule to run
	 */
	public int numberOfRunningActions() {
		return CCActionManager.sharedManager().numberOfRunningActions(this);
	}

	/// @} end of Actions

	/// @{
	/// @name Scheduler and Timer

	/**
	 * Sets a CCScheduler object that is used to schedule all "updates" and timers.
	 *
	 * @warning If you set a new CCScheduler, then previously created timers/update are going to be removed.
	 * @param scheduler     A CCShdeduler object that is used to schedule all "update" and timers.
	 */
	public void setScheduler(CCScheduler scheduler) {
		if(scheduler != m_pScheduler) {
			this.unscheduleAllSelectors();
			m_pScheduler = scheduler;
		}
	}

	/**
	 * Gets a CCSheduler object.
	 *
	 * @see setScheduler(CCScheduler*)
	 * @return A CCScheduler object.
	 */
	public CCScheduler getScheduler() {
		return m_pScheduler;
	}

	/** 
	 * Checks whether a selector is scheduled.
	 *
	 * @param selector      A function selector
	 * @return Whether the funcion selector is scheduled.
	 */
	public boolean isScheduled(UpdateCallback selector) {
		// TODO
		return false;
	}

	/** 
	 * Schedules the "update" method. 
	 *
	 * It will use the order number 0. This method will be called every frame.
	 * Scheduled methods with a lower order value will be called before the ones that have a higher order value.
	 * Only one "update" method could be scheduled per node.
	 */
	public void scheduleUpdate() {
		scheduleUpdateWithPriority(0);
	}

	/** 
	 * Schedules the "update" method with a custom priority. 
	 *
	 * This selector will be called every frame.
	 * Scheduled methods with a lower priority will be called before the ones that have a higher value.
	 * Only one "update" selector could be scheduled per node (You can't have 2 'update' selectors).
	 */
	public void scheduleUpdateWithPriority(int priority) {
		CCScheduler.sharedScheduler().scheduleUpdate(this, priority, !m_bRunning);
	}

	/* 
	 * Unschedules the "update" method.
	 * @see scheduleUpdate();
	 */
	public void unscheduleUpdate() {
		CCScheduler.sharedScheduler().unscheduleUpdate(this);
		if(m_nUpdateScriptHandler != 0) {
// TODO			CCScriptEngineManager.sharedManager().getScriptEngine().removeScriptHandler(m_nUpdateScriptHandler);
			m_nUpdateScriptHandler = 0;
		}
	}

	/**
	 * Schedules a custom selector.
	 *
	 * If the selector is already scheduled, then the interval parameter will be updated without scheduling it again.
	 * @code
	 * // firstly, implement a schedule function
	 * void MyNode::TickMe(float dt);
	 * // wrap this function into a selector via schedule_selector marco.
	 * this->schedule(schedule_selector(MyNode::TickMe), 0, 0, 0);
	 * @endcode
	 *
	 * @param interval  Tick interval in seconds. 0 means tick every frame. If interval = 0, it's recommended to use scheduleUpdate() instead.
	 * @param repeat    The selector will be excuted (repeat + 1) times, you can use kCCRepeatForever for tick infinitely.
	 * @param delay     The amount of time that the first tick will wait before execution.
	 */
	public void schedule(UpdateCallback selector, float interval, int repeat, float delay) {
		assert selector != null : "Argument selector must be non-null";
		assert interval >= 0 : "Argument interval must be positive";

		CCScheduler.sharedScheduler().schedule(selector, this, interval, !m_bRunning);
	}

	/**
	 * Schedules a custom selector with an interval time in seconds.
	 * @see schedule(SEL_SCHEDULE, float, unsigned int, float)
	 *
	 * @param selector      A function wrapped as a selector
	 * @param interval      Callback interval time in seconds. 0 means tick every frame,
	 */
	public void schedule(UpdateCallback selector, float interval) {
		this.schedule(selector, interval, ccMacros.kCCRepeatForever, 0.0f);
	}

	/**
	 * Schedules a selector that runs only once, with a delay of 0 or larger
	 * @see schedule(SEL_SCHEDULE, float, unsigned int, float)
	 *
	 * @param selector      A function wrapped as a selector
	 * @param delay         The amount of time that the first tick will wait before execution.
	 */
	public void scheduleOnce(UpdateCallback selector, float delay) {
		this.schedule(selector, 0.0f, 0, delay);
	}

	/**
	 * Schedules a custom selector, the scheduled selector will be ticked every frame
	 * @see schedule(SEL_SCHEDULE, float, unsigned int, float)
	 *
	 * @param selector      A function wrapped as a selector
	 */
	public void schedule(UpdateCallback selector) {
		this.schedule(selector, 0.0f, ccMacros.kCCRepeatForever, 0.0f);
	}

	/** 
	 * Unschedules a custom selector.
	 * @see schedule(SEL_SCHEDULE, float, unsigned int, float)
	 *
	 * @param selector      A function wrapped as a selector
	 */
	public void unschedule(UpdateCallback selector) {
		// explicit nil handling
		if(selector == null) {
			return;
		}

		CCScheduler.sharedScheduler().unschedule(selector, this);
	}

	/** 
	 * Unschedule all scheduled selectors: custom selectors, and the 'update' selector.
	 * Actions are not affected by this method.
	 */
	public void unscheduleAllSelectors() {
		CCScheduler.sharedScheduler().unscheduleAllSelectors(this);
	}

	/** 
	 * Resumes all scheduled selectors and actions.
	 * This method is called internally by onEnter
	 */
	public void resumeSchedulerAndActions() {
		CCScheduler.sharedScheduler().resume(this);
		CCActionManager.sharedManager().resume(this);
	}

	/** 
	 * Pauses all scheduled selectors and actions.
	 * This method is called internally by onExit
	 */
	public void pauseSchedulerAndActions() {
		CCScheduler.sharedScheduler().pause(this);
		CCActionManager.sharedManager().pause(this);
	}

	/* 
	 * Update method will be called automatically every frame if "scheduleUpdate" is called, and the node is "live"
	 */
	public void update(float delta) {
		if(m_nUpdateScriptHandler > 0) {
// TODO			CCScriptEngineManager.sharedManager().getScriptEngine().executeSchedule(m_nUpdateScriptHandler, fDelta, this);
		}
	}

	/// @} end of Scheduler and Timer

	/// @{
	/// @name Transformations

	/**
	 * Performs OpenGL view-matrix transformation based on position, scale, rotation and other attributes.
	 */
	public void transform(GL10 gl) {
		// transformations

		if ( ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX ) {
			// BEGIN alternative -- using cached transform
			//
			if( isTransformGLDirty_ ) {
				CGAffineTransform t = nodeToParentTransform();
				CGAffineTransform.CGAffineToGL(t, transformGL_);
				isTransformGLDirty_ = false;
			}

			// gl.glMultMatrixf(transformGL_, transformGL_.length);
			gl.glMultMatrixf(transformGL_, 0);

			if( m_fVertexZ != 0)
				gl.glTranslatef(0, 0, m_fVertexZ);

			// XXX: Expensive calls. Camera should be integrated into the cached affine matrix
			if((m_pCamera != null) && !((m_pGrid != null) && m_pGrid.isActive())) {
				boolean translate = (m_obAnchorPointInPoints.x != 0.0f || m_obAnchorPointInPoints.y != 0.0f);

				if( translate )
					gl.glTranslatef(RENDER_IN_SUBPIXEL(m_obAnchorPointInPoints.x), RENDER_IN_SUBPIXEL(m_obAnchorPointInPoints.y), 0);

				m_pCamera.locate(gl);

				if( translate )
					gl.glTranslatef(RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.x), RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.y), 0);
			}

			// END alternative

		} else {
			// BEGIN original implementation
			// 
			// translate
			if ( isRelativeAnchorPoint_ && (m_obAnchorPointInPoints.x != 0 || m_obAnchorPointInPoints.y != 0 ) )
				gl.glTranslatef( RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.x), RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.y), 0);

			if (m_obAnchorPointInPoints.x != 0 || m_obAnchorPointInPoints.y != 0)
				gl.glTranslatef( RENDER_IN_SUBPIXEL(m_obPosition.x + m_obAnchorPointInPoints.x), RENDER_IN_SUBPIXEL(m_obPosition.y + m_obAnchorPointInPoints.y), m_fVertexZ);
			else if ( m_obPosition.x !=0 || m_obPosition.y !=0 || m_fVertexZ != 0)
				gl.glTranslatef( RENDER_IN_SUBPIXEL(m_obPosition.x), RENDER_IN_SUBPIXEL(m_obPosition.y), m_fVertexZ );

			// rotate
			if (m_fRotationX != 0.0f )
				gl.glRotatef( -m_fRotationX, 0.0f, 0.0f, 1.0f );

			// scale
			if (m_fScaleX != 1.0f || m_fScaleY != 1.0f)
				gl.glScalef( m_fScaleX, m_fScaleY, 1.0f );

			if ( m_pCamera!=null && !(m_pGrid!=null && m_pGrid.isActive()) )
				m_pCamera.locate(gl);

			// restore and re-position point
			if (m_obAnchorPointInPoints.x != 0.0f || m_obAnchorPointInPoints.y != 0.0f)
				gl.glTranslatef(RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.x), RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.y), 0);

			//
			// END original implementation
		}
	}

	/**
	 * Performs OpenGL view-matrix transformation of it's ancestors.
	 * Generally the ancestors are already transformed, but in certain cases (eg: attaching a FBO)
	 * It's necessary to transform the ancestors again.
	 */
	public void transformAncestors(GL10 gl) {
		if (m_pParent != null) {
			m_pParent.transformAncestors(gl);
			m_pParent.transform(gl);
		}
	}

	/**
	 * Calls children's updateTransform() method recursively.
	 *
	 * This method is moved from CCSprite, so it's no longer specific to CCSprite.
	 * As the result, you apply CCSpriteBatchNode's optimization on your customed CCNode.
	 * e.g., batchNode->addChild(myCustomNode), while you can only addChild(sprite) before.
	 */
	public void updateTransform() {
		// Recursively iterate over children
		for(CCNode child : m_pChildren) {
			child.updateTransform();
		}
	}

	/** 
	 * Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
	 * The matrix is in Pixels.
	 */
	public CGAffineTransform nodeToParentTransform() {
		if (m_bTransformDirty) {
			CGPoint zero = CGPoint.getZero();
			m_sTransform.setToIdentity();

			if (!isRelativeAnchorPoint_ && !CGPoint.equalToPoint(m_obAnchorPointInPoints, zero)) {
				m_sTransform.translate(m_obAnchorPointInPoints.x, m_obAnchorPointInPoints.y);
			}

			if (!CGPoint.equalToPoint(m_obPosition, zero))
				m_sTransform.translate(m_obPosition.x, m_obPosition.y);

			if (m_fRotationX != 0)
				m_sTransform.rotate(-ccMacros.CC_DEGREES_TO_RADIANS(m_fRotationX));

			if (m_fSkewX != 0 || m_fSkewY != 0) {
				/** create a skewed coordinate system */
				CGAffineTransform skew = CGAffineTransform.make(1.0f, MathLib.tan(ccMacros.CC_DEGREES_TO_RADIANS(m_fSkewY)), MathLib.tan(ccMacros.CC_DEGREES_TO_RADIANS(m_fSkewX)), 1.0f, 0.0f, 0.0f);
				/** apply the skew to the transform */
				m_sTransform = m_sTransform.getTransformConcat(skew);
			}

			if( ! (m_fScaleX == 1 && m_fScaleY == 1) ) 
				m_sTransform.scale(m_fScaleX, m_fScaleY);

			if (!CGPoint.equalToPoint(m_obAnchorPointInPoints, zero))
				m_sTransform.translate(-m_obAnchorPointInPoints.x, -m_obAnchorPointInPoints.y);

			m_bTransformDirty = false;
		}

		return m_sTransform;
	}

	/**
	 * Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates.
	 * The matrix is in Pixels.
	 */
	public CGAffineTransform parentToNodeTransform() {
		if (m_bInverseDirty) {
			CGAffineTransformUtil.inverse(nodeToParentTransform(), m_sInverse);
			m_bInverseDirty = false;
		}

		return m_sInverse;
	}

	/** 
	 * Returns the world affine transform matrix. The matrix is in Pixels.
	 */
	private CGAffineTransform nodeToWorldTransform() {
		CGAffineTransform t = new CGAffineTransform(nodeToParentTransform());

		for (CCNode p = m_pParent; p != null; p = p.m_pParent) {
			// t = t.getTransformConcat(p.nodeToParentTransform());
			t = t.preConcatenate(p.nodeToParentTransform());
		}

		return t;
	}

	/** 
	 * Returns the inverse world affine transform matrix. The matrix is in Pixels.
	 */
	public CGAffineTransform worldToNodeTransform() {
		return nodeToWorldTransform().getTransformInvert();
	}

	/// @} end of Transformations

	/// @{
	/// @name Coordinate Converters

	/** 
	 * Converts a Point to node (local) space coordinates. The result is in Points.
	 */
	public CGPoint convertToNodeSpace(CGPoint worldPoint) {
		return CGPoint.applyAffineTransform(worldPoint, worldToNodeTransform());
	}

	/** 
	 * Converts a Point to world space coordinates. The result is in Points.
	 */
	public CGPoint convertToWorldSpace(CGPoint nodePoint) {
		return CGPoint.applyAffineTransform(nodePoint, nodeToWorldTransform());
	}

	/** 
	 * Converts a Point to node (local) space coordinates. The result is in Points.
	 * treating the returned/received node point as anchor relative.
	 */
	public CGPoint convertToNodeSpaceAR(CGPoint worldPoint) {
		CGPoint nodePoint = convertToNodeSpace(worldPoint);
		return CGPoint.ccpSub(nodePoint, m_obAnchorPointInPoints);
	}

	/** 
	 * Converts a local Point to world space coordinates.The result is in Points.
	 * treating the returned/received node point as anchor relative.
	 */
	public CGPoint convertToWorldSpaceAR(CGPoint nodePoint) {
		CGPoint pt = CGPoint.ccpAdd(nodePoint, m_obAnchorPointInPoints);
		return convertToWorldSpace(pt);
	}

	/** 
	 * convenience methods which take a CCTouch instead of CCPoint
	 */
	public CGPoint convertTouchToNodeSpace(CCTouch touch) {
		CGPoint point = touch.getLocation();
		return this.convertToNodeSpace(point);
	}

	/** 
	 * converts a CCTouch (world coordinates) into a local coordinate. This method is AR (Anchor Relative).
	 */
	public CGPoint convertTouchToNodeSpaceAR(CCTouch touch) {
		CGPoint point = touch.getLocation();
		return this.convertToNodeSpaceAR(point);
	}

	/**
	 *  Sets the additional transform.
	 *
	 *  @note The additional transform will be concatenated at the end of nodeToParentTransform.
	 *        It could be used to simulate `parent-child` relationship between two nodes (e.g. one is in BatchNode, another isn't).
	 *  @code
	 *        // create a batchNode
	 *        CCSpriteBatchNode* batch= CCSpriteBatchNode::create("Icon-114.png");
	 *        this->addChild(batch);
	 *     
	 *        // create two sprites, spriteA will be added to batchNode, they are using different textures.
	 *        CCSprite* spriteA = CCSprite::createWithTexture(batch->getTexture());
	 *        CCSprite* spriteB = CCSprite::create("Icon-72.png");
	 *
	 *        batch->addChild(spriteA); 
	 *     
	 *        // We can't make spriteB as spriteA's child since they use different textures. So just add it to layer.
	 *        // But we want to simulate `parent-child` relationship for these two node.
	 *        this->addChild(spriteB); 
	 *
	 *        //position
	 *        spriteA->setPosition(ccp(200, 200));
	 *     
	 *        // Gets the spriteA's transform.
	 *        CCAffineTransform t = spriteA->nodeToParentTransform();
	 *     
	 *        // Sets the additional transform to spriteB, spriteB's postion will based on its pseudo parent i.e. spriteA.
	 *        spriteB->setAdditionalTransform(t);
	 *
	 *        //scale
	 *        spriteA->setScale(2);
	 *     
	 *        // Gets the spriteA's transform.
	 *        t = spriteA->nodeToParentTransform();
	 *     
	 *        // Sets the additional transform to spriteB, spriteB's scale will based on its pseudo parent i.e. spriteA.
	 *        spriteB->setAdditionalTransform(t);
	 *
	 *        //rotation
	 *        spriteA->setRotation(20);
	 *     
	 *        // Gets the spriteA's transform.
	 *        t = spriteA->nodeToParentTransform();
	 *     
	 *        // Sets the additional transform to spriteB, spriteB's rotation will based on its pseudo parent i.e. spriteA.
	 *        spriteB->setAdditionalTransform(t);
	 *  @endcode
	 */
	public void setAdditionalTransform(CGAffineTransform additionalTransform) {
		m_sAdditionalTransform = additionalTransform;
		m_bTransformDirty = true;
		m_bAdditionalTransformDirty = true;
	}

	/// @} end of Coordinate Converters

	/// lazy allocs
	private void childrenAlloc() {
		m_pChildren = Collections.synchronizedList(new ArrayList<CCNode>(4));
	}

	/// helper that reorder a child
	private void insertChild(CCNode child, int z) {
		m_bReorderChildDirty = true;
		m_pChildren.add(child);
		child._setZOrder(z);
	}

	/// Removes a child, call child->onExit(), do cleanup, remove it from children array.
	private void detachChild(CCNode child, boolean doCleanup) {
		// IMPORTANT:
		//  -1st do onExit
		//  -2nd cleanup
		if (m_bRunning) {
			child.onExitTransitionDidStart();
			child.onExit();
		}

		// If you don't do cleanup, the child's actions will not get removed and the
		// its scheduledSelectors_ dict will not get released!
		if (doCleanup) {
			child.cleanup();
		}

		// set parent nil at the end
		child.setParent(null);

		m_pChildren.remove(child);
	}

	/// Convert cocos2d coordinates to UI windows coordinate.
	private CGPoint convertToWindowSpace(CGPoint nodePoint) {
		CGPoint worldPoint = this.convertToWorldSpace(nodePoint);
		return CCDirector.sharedDirector().convertToUI(worldPoint);
	}

	private static Comparator<CCNode> zOrderComparator = new Comparator<CCNode>() {
		@Override
		public int compare(CCNode o1, CCNode o2) {
			int ret = o1.m_nZOrder - o2.m_nZOrder;
			if(ret == 0) {
				ret = (o1.m_uOrderOfArrival < o2.m_uOrderOfArrival) ? -1 : 0;
			}
			return ret;
		}
	};

	protected float m_fRotationX;					///< rotation angle on x-axis
	protected float m_fRotationY;					///< rotation angle on x-axis

	protected float m_fScaleX;						///< scaling factor on x-axis
	protected float m_fScaleY;						///< scaling factor on y-axis

	protected float m_fVertexZ;						///< OpenGL real Z vertex

	protected CGPoint m_obPosition;					///< position of the node

	protected float m_fSkewX;						///< skew angle on x-axis
	protected float m_fSkewY;						///< skew angle on y-axis

	protected CGPoint m_obAnchorPointInPoints;		///< anchor point in points
	protected CGPoint m_obAnchorPoint;				///< anchor point normalized (NOT in points)

	protected CGSize m_obContentSize;				///< untransformed size of the node

	protected CGAffineTransform m_sAdditionalTransform; ///< transform
	protected CGAffineTransform m_sTransform;		///< transform
	protected CGAffineTransform m_sInverse;			///< transform

	protected CCCamera m_pCamera;					///< a camera

	protected CCGridBase m_pGrid;					///< a grid

	protected int m_nZOrder;						///< z-order value that affects the draw order

	protected List<CCNode> m_pChildren;				///< array of children nodes
	protected CCNode m_pParent;						///< weak reference to parent node

	protected int m_nTag;							///< a tag. Can be any number you assigned just to identify this node

	protected Object m_pUserObject;						///< A user assingned void pointer, Can be point to any cpp object

// TODO	protected CCGLProgram m_pShaderProgram;			///< OpenGL shader

// TODO	protected ccGLServerState m_eGLServerState;		///< OpenGL servier side state

	protected int m_uOrderOfArrival;				///< used to preserve sequence while sorting children with the same zOrder

	protected CCScheduler m_pScheduler;				///< scheduler used to schedule timers and updates

	protected CCActionManager m_pActionManager;		///< a pointer to ActionManager singleton, which is used to handle all the actions

	// Is running
	protected boolean m_bRunning;					///< is running

	protected boolean m_bTransformDirty;			///< transform dirty flag
	protected boolean m_bInverseDirty;				///< transform dirty flag
	protected boolean m_bAdditionalTransformDirty;	///< The flag to check whether the additional transform is dirty
	protected boolean m_bVisible;					///< is this node visible

	protected boolean m_bIgnoreAnchorPointForPosition;	///< true if the Anchor Point will be (0,0) when you position the CCNode, false otherwise.
														///< Used by CCLayer and CCScene.

	protected boolean m_bReorderChildDirty;			///< children order dirty flag

	protected int m_nScriptHandler;					///< script handler for onEnter() & onExit(), used in Javascript binding and Lua binding.
	protected int m_nUpdateScriptHandler;			///< script handler for update() callback per frame, which is invoked from lua & javascript.
// TODO	protected ccScriptType m_eScriptType;			///< type of script binding, lua or javascript

	// XXX: Yes, nodes might have a sort problem once every 15 days if the game runs at 60 FPS and each frame sprites are reordered.
	private static int s_globalOrderOfArrival = 1;


	// TODO legacy -->

	public CGPoint getPositionRef() {
		return m_obPosition;
	}

    /** The anchorPoint in absolute pixels.
      Since v0.8 you can only read it. If you wish to modify it, use anchorPoint instead
    */
    public CGPoint getAnchorPointInPixels() {
        return CGPoint.make(m_obAnchorPointInPoints.x, m_obAnchorPointInPoints.y);
    }
    
	// If YES the transformtions will be relative to (-transform.x, -transform.y).
	// Sprites, Labels and any other "small" object uses it.
	// Scenes, Layers and other "whole screen" object don't use it.
    private boolean isRelativeAnchorPoint_;

    /** If YES the transformtions will be relative to it's anchor point.
       Sprites, Labels and any other sizeble object use it have it enabled by default.
       Scenes, Layers and other "whole screen" object don't use it, have it disabled by default.
    */
    public void setRelativeAnchorPoint(boolean newValue) {
        isRelativeAnchorPoint_ = newValue;
        m_bTransformDirty = m_bInverseDirty = true;
        if (ccConfig.CC_NODE_TRANSFORM_USING_AFFINE_MATRIX) {
            isTransformGLDirty_ = true;
        }
    }

    public boolean getRelativeAnchorPoint() {
        return isRelativeAnchorPoint_;
    }

    /** The untransformed size of the node.
     The contentSize remains the same no matter the node is scaled or rotated.
     All nodes has a size. Layer and Scene has the same size of the screen.
     @since v0.8
     */
	public void setContentSize(float w, float h) {
		setContentSize(CGSize.make(w, h));
    }
	
    public CGSize getContentSize2() {
        return CGSize.make(m_obContentSize.width, m_obContentSize.height);
    }
    
    public CGSize getContentSizeRef() {
        return m_obContentSize;
    }

    /** anchorPoint is the point around which all transformations and positioning manipulations take place.
      It's like a pin in the node where it is "attached" to its parent.
      The anchorPoint is normalized, like a percentage.
        (0,0) means the bottom-left corner and (1,1) means the top-right corner.
      But you can use values higher than (1,1) and lower than (0,0) too.
      The default anchorPoint is (0.5,0.5), so it starts in the center of the node.
      @since v0.8
    */
    public void setAnchorPoint(float x, float y) {
    	setAnchorPoint(CGPoint.ccp(x, y));
    }
   
    
    public CGPoint getAnchorPointRef() {
        return m_obAnchorPoint;
    }
    
    // #if	CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
	private float []	transformGL_; // [16];
	
    // #endif

    //#if	CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
	private boolean isTransformGLDirty_;
    //#endif


    // initializators
    /** allocates and initializes a node.
      The node will be created as "autorelease".
    */
    public static CCNode node() {
        return create();
    }


    /**
     * Remove myself from the parent, for action CCCallFunc
     */
    public void removeSelf() {
    	this.removeFromParentAndCleanup(true);
    }

    /** schedules a selector.
      The scheduled selector will be ticked every frame
    */
    public void schedule(String selector) {
        schedule(selector, 0);
    }

    /** schedules a custom selector with an interval time in seconds.
    If time is 0 it will be ticked every frame.
    If time is 0, it is recommended to use 'scheduleUpdate' instead.
    */
    public void schedule(String selector, float interval) {
        assert selector != null : "Argument selector must be non-null";
        assert interval >= 0 : "Argument interval must be positive";
        
        CCScheduler.sharedScheduler().schedule(selector, this, interval, !m_bRunning);
    }


    /* unschedules a custom selector.*/
    public void unschedule(String selector) {
        // explicit null handling
        if (selector == null)
            return;

        CCScheduler.sharedScheduler().unschedule(selector, this);
    }
    
    private void nodeToWorldTransform(CGAffineTransform ret) {
        ret.setTransform(nodeToParentTransform());

        for (CCNode p = m_pParent; p != null; p = p.m_pParent) {
        	CGAffineTransformUtil.preConcate(ret, p.nodeToParentTransform());
        }
    }
    
    /**
     * This is analog method, result is written to ret. No garbage.
     */
    private void worldToNodeTransform(CGAffineTransform ret) {
    	nodeToWorldTransform(ret);
        CGAffineTransformUtil.inverse(ret);
    }

    /** converts a world coordinate to local coordinate
      @since v0.7.1
    */
    public CGPoint convertToNodeSpace(float x, float y) {
        OneClassPool<CGAffineTransform> pool = PoolHolder.getInstance().getCGAffineTransformPool();
        
        CGAffineTransform temp = pool.get();
        worldToNodeTransform(temp);
        
        CGPoint ret = new CGPoint();
    	CGPointUtil.applyAffineTransform(x, y, temp, ret);
    	
        pool.free(temp);
        return ret;
    }
    
    /**
     * This is analog method, result is written to ret. No garbage.
     */
    public void convertToNodeSpace(CGPoint p, CGPoint ret) {
    	convertToNodeSpace(p.x, p.y, ret);
    }
    
    /**
     * This is analog method, result is written to ret. No garbage.
     */
    public void convertToNodeSpace(float x, float y, CGPoint ret) {
        OneClassPool<CGAffineTransform> pool = PoolHolder.getInstance().getCGAffineTransformPool();
        
        CGAffineTransform temp = pool.get();
        worldToNodeTransform(temp);
        
        CGPointUtil.applyAffineTransform(x, y, temp, ret);
        
        pool.free(temp);
    }

    /** converts local coordinate to world space
      @since v0.7.1
    */
    public CGPoint convertToWorldSpace(float x, float y) {
        CGPoint nodePoint = CGPoint.make(x, y);
        return CGPoint.applyAffineTransform(nodePoint, nodeToWorldTransform());
    }
    
    /**
     * This is analog method, result is written to ret. No garbage.
     */
    public void convertToWorldSpace(float x, float y , CGPoint ret) {
        OneClassPool<CGAffineTransform> pool = PoolHolder.getInstance().getCGAffineTransformPool();
        
        CGAffineTransform temp = pool.get();
        nodeToWorldTransform(temp);
        
        CGPointUtil.applyAffineTransform(x, y, temp, ret);
        
        pool.free(temp);
    }
    
    /** converts a world coordinate to local coordinate
      treating the returned/received node point as anchor relative
      @since v0.7.1
    */
    public CGPoint convertToNodeSpaceAR(float x, float y) {
        CGPoint nodePoint = convertToNodeSpace(x, y);
        return CGPoint.ccpSub(nodePoint, m_obAnchorPointInPoints);
    }

    /** converts local coordinate to world space
      treating the returned/received node point as anchor relative
      @since v0.7.1
    */
    public CGPoint convertToWorldSpaceAR(float x, float y) {
        CGPoint nodePoint = CGPoint.make(x, y);
        nodePoint = CGPoint.ccpAdd(nodePoint, m_obAnchorPointInPoints);
        return convertToWorldSpace(nodePoint.x, nodePoint.y);
    }

    // convenience methods which take a MotionEvent instead of PointF
    /** convenience methods which take a UITouch instead of CGPoint
      @since v0.7.1
    */
    public CGPoint convertTouchToNodeSpace(MotionEvent event) {
    	OneClassPool<CGPoint> pool = PoolHolder.getInstance().getCGPointPool();
    	CGPoint point = pool.get();
    	
    	int action = event.getAction();
		int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
        if(Build.VERSION.SDK_INT >= 5) {
        	CCDirector.sharedDirector().convertToGL(Util5.getX(event, pid), Util5.getY(event, pid), point);
        } else {
        	CCDirector.sharedDirector().convertToGL(event.getX(), event.getY(), point);
        }
    	
    	float x = point.x, y = point.y;
    	pool.free(point);
    	
        return convertToNodeSpace(x, y);
    }
    
    /**
     * This is analog method, result is written to ret. No garbage.
     */
    public void convertTouchToNodeSpace(MotionEvent event, CGPoint ret) {
    	int action = event.getAction();
		int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
        if(Build.VERSION.SDK_INT >= 5) {
        	CCDirector.sharedDirector().convertToGL(Util5.getX(event, pid), Util5.getY(event, pid), ret);
        } else {
        	CCDirector.sharedDirector().convertToGL(event.getX(), event.getY(), ret);
        }
    	
        convertToNodeSpace(ret.x, ret.y, ret);
    }

    /** converts a UITouch (world coordinates) into a local coordiante. This method is AR (Anchor Relative).
      @since v0.7.1
    */
    public CGPoint convertTouchToNodeSpaceAR(MotionEvent event) {
    	OneClassPool<CGPoint> pool = PoolHolder.getInstance().getCGPointPool();
    	CGPoint point = pool.get();
    	
    	int action = event.getAction();
		int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
        if(Build.VERSION.SDK_INT >= 5) {
        	CCDirector.sharedDirector().convertToGL(Util5.getX(event, pid), Util5.getY(event, pid), point);
        } else {
        	CCDirector.sharedDirector().convertToGL(event.getX(), event.getY(), point);
        }
    	
    	float x = point.x, y = point.y;
    	pool.free(point);
    	
        return convertToNodeSpaceAR(x, y);
    }

    public interface CocosNodeSize {
        public float getWidth();

        public float getHeight();
    }

    /** should we do this?
    @Override
    public void finalize() {
        Log.w( "cocos2d: deallocing " + self);

        // attributes
        camera_ = null;
        grid_ = null;

        // children
        if (children) {
            for (CCNode child: children) {
                child.parent = null;
            }
        }

        children_.clear();
        children_ = null;

        super.finalize();
    }*/

    @Override
    public String toString() {
        return "<instance of " + this.getClass() + "| Tag = " + m_nTag + ">";
    }


    private static final float RENDER_IN_SUBPIXEL(float f) {
        if (ccConfig.CC_COCOSNODE_RENDER_SUBPIXEL) {
            return f;
        } else {
            int i = (int) f;
            return (float)i;
        }
    }
}
