package org.cocos2d.include;

import javax.microedition.khronos.opengles.GL10;

import org.cocos2d.CCDirector;
import org.cocos2d.cocoa.CCGeometry.CCPoint;
import org.cocos2d.cocoa.CCGeometry.CCRect;
import org.cocos2d.cocoa.CCGeometry.CCSize;
import org.cocos2d.lib.Cocos2dGLSurfaceView;
import org.cocos2d.opengl.CCGLSurfaceView;
import org.cocos2d.shaders.CCShaderCache;
import org.cocos2d.support.CCProfiling;
import org.cocos2d.support.CCProfiling.CCProfiler;
import org.cocos2d.utils.CCFormatter;

import android.app.Activity;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.ViewGroup;

/**
 @file
 cocos2d helper macros
 */
public class ccMacros {

	public static void CCAssert(boolean cond, String msg) {
		if(ccConfig.COCOS2D_DEBUG > 0) {
			assert cond : msg;
		}
	}

	/** @def CC_SWAP
	 * simple macro that swaps 2 variables
	 */
	public static void CC_SWAP(Object x, Object y) {
		Object temp = x;
		x = y;
		y = temp;
	}

	/** @def CCRANDOM_MINUS1_1
	 * returns a random float between -1 and 1
	 */
	public static float CCRANDOM_MINUS1_1() {
		return ((2.0f * ((float) Math.random())) - 1.0f);
	}

	/** @def CCRANDOM_0_1
	 * returns a random float between 0 and 1
	 */
	public static float CCRANDOM_0_1() {
		return (float) Math.random();
	}

	/** @def CC_DEGREES_TO_RADIANS
	 * converts degrees to radians
	 */
	public static float CC_DEGREES_TO_RADIANS(float angle) {
		return (angle * 0.01745329252f); // PI / 180
	}

	/** @def CC_RADIANS_TO_DEGREES
	 * converts radians to degrees
	 */
	public static float CC_RADIANS_TO_DEGREES(float angle) {
		return (angle * 57.29577951f); // PI * 180
	}

	public static int kCCRepeatForever = (Integer.MAX_VALUE - 1);

	/** @def CC_BLEND_SRC
	 * default gl blend src function. Compatible with premultiplied alpha images.
	 */
	public static int CC_BLEND_SRC = GLES20.GL_ONE;
	public static int CC_BLEND_DST = GLES20.GL_ONE_MINUS_SRC_ALPHA;

	/** @def CC_NODE_DRAW_SETUP
	 * Helpful macro that setups the GL server state, the correct GL program and sets the Model View Projection matrix
	 * @since v2.0
	 */
	public static void CC_NODE_DRAW_SETUP() {
		do {
/* TODO
			ccGLEnable(m_eGLServerState);
			CCAssert(getShaderProgram(), "No shader program set for this node");
			{
				getShaderProgram().use();
				getShaderProgram().setUniformsForBuiltins();
			}
*/
		} while(false);
	}

	/** @def CC_DIRECTOR_END
	 * Stops and removes the director from memory.
	 * Removes the EAGLView from its parent
	 * 
	 * @since v0.99.4
	 */
	public static final void CC_DIRECTOR_END() {
		do {
			CCDirector __director = CCDirector.sharedDirector();

			// TODO legacy -->
			GLSurfaceView view = __director.getOpenGLView();
			ViewGroup vg = (ViewGroup)view.getParent();
			vg.removeView(view);
			// TODO legacy <--

			__director.end();
		} while(false);
	}

	/** @def CC_CONTENT_SCALE_FACTOR
	 * On Mac it returns 1;
	 * On iPhone it returns 2 if RetinaDisplay is On. Otherwise it returns 1
	 */
	public static float CC_CONTENT_SCALE_FACTOR() {
		return CCDirector.sharedDirector().getContentScaleFactor();
	}

	/****************************/
	/** RETINA DISPLAY ENABLED **/
	/****************************/

	/** @def CC_RECT_PIXELS_TO_POINTS
	 * Converts a rect in pixels to points
	 */
	public static CCRect CC_RECT_PIXELS_TO_POINTS(CCRect __rect_in_pixels__) {
		return CCRect.Make( (__rect_in_pixels__).origin.x / CC_CONTENT_SCALE_FACTOR(), (__rect_in_pixels__).origin.y / CC_CONTENT_SCALE_FACTOR(),
				(__rect_in_pixels__).size.width / CC_CONTENT_SCALE_FACTOR(), (__rect_in_pixels__).size.height / CC_CONTENT_SCALE_FACTOR() );
	}

	/** @def CC_RECT_POINTS_TO_PIXELS
	 * Converts a rect in points to pixels
	 */
	public static CCRect CC_RECT_POINTS_TO_PIXELS(CCRect __rect_in_points_points__) {
		return CCRect.Make( (__rect_in_points_points__).origin.x * CC_CONTENT_SCALE_FACTOR(), (__rect_in_points_points__).origin.y * CC_CONTENT_SCALE_FACTOR(),
				(__rect_in_points_points__).size.width * CC_CONTENT_SCALE_FACTOR(), (__rect_in_points_points__).size.height * CC_CONTENT_SCALE_FACTOR() );
	}

	/** @def CC_POINT_PIXELS_TO_POINTS
	 * Converts a rect in pixels to points
	 */
	public static CCPoint CC_POINT_PIXELS_TO_POINTS(CCPoint __pixels__) {
		return CCPoint.Make( (__pixels__).x / CC_CONTENT_SCALE_FACTOR(), (__pixels__).y / CC_CONTENT_SCALE_FACTOR());
	}

	/** @def CC_POINT_POINTS_TO_PIXELS
	 Converts a rect in points to pixels
	 */
	public static CCPoint CC_POINT_POINTS_TO_PIXELS(CCPoint __points__) {
		return CCPoint.Make( (__points__).x * CC_CONTENT_SCALE_FACTOR(), (__points__).y * CC_CONTENT_SCALE_FACTOR());
	}

	/** @def CC_POINT_PIXELS_TO_POINTS
	 * Converts a rect in pixels to points
	 */
	public static CCSize CC_SIZE_PIXELS_TO_POINTS(CCSize __size_in_pixels__) {
		return CCSize.Make( (__size_in_pixels__).width / CC_CONTENT_SCALE_FACTOR(), (__size_in_pixels__).height / CC_CONTENT_SCALE_FACTOR());
	}

	/** @def CC_POINT_POINTS_TO_PIXELS
	 * Converts a rect in points to pixels
	 */
	public static CCSize CC_SIZE_POINTS_TO_PIXELS(CCSize __size_in_points__) {
		return CCSize.Make( (__size_in_points__).width * CC_CONTENT_SCALE_FACTOR(), (__size_in_points__).height * CC_CONTENT_SCALE_FACTOR());
	}

//	public static final float FLT_EPSILON = 1.192092896e-07F;
	public static final float FLT_EPSILON = 0.000001f;	// TODO legacy

	/**********************/
	/** Profiling Macros **/
	/**********************/

	public static void CC_PROFILER_DISPLAY_TIMERS() {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiler.sharedProfiler().displayTimers();
		}
	}

	public static void CC_PROFILER_PURGE_ALL() {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiler.sharedProfiler().releaseAllTimers();
		}
	}

	public static void CC_PROFILER_START(String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiling.CCProfilingBeginTimingBlock(__name__);
		}
	}

	public static void CC_PROFILER_STOP(String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiling.CCProfilingEndTimingBlock(__name__);
		}
	}

	public static void CC_PROFILER_RESET(String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiling.CCProfilingResetTimingBlock(__name__);
		}
	}

	public static void CC_PROFILER_START_CATEGORY(boolean __cat__, String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			if(__cat__) CCProfiling.CCProfilingBeginTimingBlock(__name__);
		}
	}

	public static void CC_PROFILER_STOP_CATEGORY(boolean __cat__, String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			if(__cat__) CCProfiling.CCProfilingEndTimingBlock(__name__);
		}
	}

	public static void CC_PROFILER_RESET_CATEGORY(boolean __cat__, String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			if(__cat__) CCProfiling.CCProfilingResetTimingBlock(__name__);
		}
	}

	public static void CC_PROFILER_START_INSTANCE(int __id__, String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiling.CCProfilingBeginTimingBlock(CCFormatter.format("%08X - %s", __id__, __name__));
		}
	}

	public static void CC_PROFILER_STOP_INSTANCE(int __id__, String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiling.CCProfilingEndTimingBlock(CCFormatter.format("%08X - %s", __id__, __name__));
		}
	}

	public static void CC_PROFILER_RESET_INSTANCE(int __id__, String __name__) {
		if(ccConfig.CC_ENABLE_PROFILERS != 0) {
			CCProfiling.CCProfilingResetTimingBlock(CCFormatter.format("%08X - %s", __id__, __name__));
		}
	}

	public static void CHECK_GL_ERROR_DEBUG() {
		if(ccConfig.COCOS2D_DEBUG != 0) {
			do {
				int __error = GLES20.glGetError();
				if(__error != 0) {
					CCLOG("CHECK_GL_ERROR_DEBUG", CCFormatter.format("OpenGL error 0x%04X", __error));
				}
			} while (false);
		}
	}

	/** @def CC_INCREMENT_GL_DRAWS_BY_ONE
	 * Increments the GL Draws counts by one.
	 * The number of calls per frame are displayed on the screen when the CCDirector's stats are enabled.
	 */
	public static void CC_INCREMENT_GL_DRAWS(int __n__) {
		CCDirector.g_uNumberOfDraws += __n__;
	}

	/*******************/
	/** Notifications **/
	/*******************/
	/** @def CCAnimationFrameDisplayedNotification
	 * Notification name when a CCSpriteFrame is displayed
	 */
	public static final String CCAnimationFrameDisplayedNotification = "CCAnimationFrameDisplayedNotification";

	// TODO legacy -->

	/*
	 * if COCOS2D_DEBUG is not defined, or if it is 0 then
	 *	all CCLOGXXX macros will be disabled
	 *
	 * if COCOS2D_DEBUG==1 then:
	 *		CCLOG() will be enabled
	 *		CCLOGERROR() will be enabled
	 *		CCLOGINFO()	will be disabled
	 *
	 * if COCOS2D_DEBUG==2 or higher then:
	 *		CCLOG() will be enabled
	 *		CCLOGERROR() will be enabled
	 *		CCLOGINFO()	will be enabled 
	 */
	public static final void CCLOG(final String logName, final String logStr) {
		if (ccConfig.COCOS2D_DEBUG >= 1) {
			Log.d(logName, logStr);
		}
	}

	public static final void CCLOGINFO(final String logName, final String logStr) {
		if (ccConfig.COCOS2D_DEBUG >= 1) {
			Log.i(logName, logStr);
		}
	}

	public static final void CCLOGERROR(final String logName, final String logStr) {
		if (ccConfig.COCOS2D_DEBUG >= 2) {
			Log.e(logName, logStr);
		}
	}

	public static final int INT_MIN = -2147483648;
	public static final int CC_MAX_PARTICLE_SIZE = 64;

	/** @def M_PI_2
	 * 			Math.PI divided by 2
	 */
	public static final float M_PI_2 = (float)(Math.PI / 2);

	/** @def CC_ENABLE_DEFAULT_GL_STATES
	 * GL states that are enabled:
	 * - GL_TEXTURE_2D
	 * - GL_VERTEX_ARRAY
	 * - GL_TEXTURE_COORD_ARRAY
	 * - GL_COLOR_ARRAY
	 */
	public static final void CC_ENABLE_DEFAULT_GL_STATES(GL10 gl) {
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		gl.glEnable(GL10.GL_TEXTURE_2D);
	}

    /** @def CC_DISABLE_DEFAULT_GL_STATES 
      Disable default GL states:
      - GL_TEXTURE_2D
      - GL_VERTEX_ARRAY
      - GL_TEXTURE_COORD_ARRAY
      - GL_COLOR_ARRAY
      */
    public static final void CC_DISABLE_DEFAULT_GL_STATES(GL10 gl) {
        gl.glDisable(GL10.GL_TEXTURE_2D);
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    }

    /** @def CC_DIRECTOR_INIT
      - Initializes an EAGLView with 0-bit depth format, and RGB565 render buffer.
      - The EAGLView view will have multiple touches disabled.
      - It will create a UIWindow and it will assign it the 'window' variable. 'window' must be declared before calling this marcro.
      - It will parent the EAGLView to the created window
      - If the firmware >= 3.1 it will create a Display Link Director. Else it will create an NSTimer director.
      - It will try to run at 60 FPS.
      - The FPS won't be displayed.
      - The orientation will be portrait.
      - It will connect the director with the EAGLView.

    IMPORTANT: If you want to use another type of render buffer (eg: RGBA8)
    or if you want to use a 16-bit or 24-bit depth buffer, you should NOT
    use this macro. Instead, you should create the EAGLView manually.

    @since v0.99.4
    */
    // not supported yet...
    public static final void CC_DIRECTOR_INIT(Activity app) {
    	CCDirector director = CCDirector.sharedDirector();
    	director.setDeviceOrientation(CCDirector.kCCDeviceOrientationPortrait);
    	director.setDisplayFPS(false);
    	director.setAnimationInterval(1.0/60);
    	
    	CCGLSurfaceView sv = new CCGLSurfaceView(app);
    	
    	director.attachInView(sv);
    	app.setContentView(sv);
    }
}
