/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011      Zynga Inc.

http://www.cocos2d-x.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package org.cocos2d.base_nodes;

import javax.microedition.khronos.opengles.GL10;

import org.cocos2d.CCDirector;
import org.cocos2d.cocoa.CCGeometry.CCSize;
import org.cocos2d.include.ccConfig;
import org.cocos2d.include.ccMacros;
import org.cocos2d.include.CCProtocols.CCTextureProtocol;
import org.cocos2d.shaders.CCGLProgram;
import org.cocos2d.shaders.CCShaderCache;
import org.cocos2d.shaders.ccGLStateCache;
import org.cocos2d.textures.CCTexture2D;
import org.cocos2d.textures.CCTextureAtlas;
import org.cocos2d.textures.CCTextureCache;
import org.cocos2d.include.ccTypes.ccBlendFunc;
import org.cocos2d.include.ccTypes.ccColor3B;

import android.opengl.GLES20;

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

/** @brief CCAtlasNode is a subclass of CCNode that implements the CCRGBAProtocol and CCTextureProtocol protocol
 * 
 * It knows how to render a TextureAtlas object.
 * If you are going to render a TextureAtlas consider subclassing CCAtlasNode (or a subclass of CCAtlasNode)
 * 
 * All features from CCNode are valid, plus the following features:
 * - opacity and RGB colors
 */
public class CCAtlasNode extends CCNodeRGBA implements CCTextureProtocol {
	private static final String TAG = CCAtlasNode.class.getSimpleName();

	//! chars per row
	protected int m_uItemsPerRow;
	//! chars per column
	protected int m_uItemsPerColumn;

	//! width of each char
	protected int m_uItemWidth;
	//! height of each char
	protected int m_uItemHeight;

	protected ccColor3B m_tColorUnmodified = new ccColor3B();

	protected CCTextureAtlas m_pTextureAtlas;

	public CCTextureAtlas getTextureAtlas() {
		return m_pTextureAtlas;
	}

	public void setTextureAtlas(CCTextureAtlas textureAtlas) {
		m_pTextureAtlas = textureAtlas;
	}

	// protocol variables
	protected boolean m_bIsOpacityModifyRGB;

	protected ccBlendFunc m_tBlendFunc = new ccBlendFunc();

	public ccBlendFunc getBlendFunc() {
		return m_tBlendFunc;
	}

	public void setBlendFunc(final ccBlendFunc blendFunc) {
		m_tBlendFunc.set(blendFunc);
	}

	// quads to draw
	protected int m_uQuadsToDraw;

	public int getQuadsToDraw() {
		return m_uQuadsToDraw;
	}

	public void setQuadsToDraw(int uQuadsToDraw) {
		m_uQuadsToDraw = uQuadsToDraw;
	}

	// color uniform
	int m_nUniformColor;

	public CCAtlasNode() {
		m_uItemsPerRow = 0;
		m_uItemsPerColumn = 0;
		m_uItemWidth = 0;
		m_uItemHeight = 0;
		m_pTextureAtlas = null;
		m_bIsOpacityModifyRGB = false;
		m_uQuadsToDraw = 0;
		m_nUniformColor = 0;
	}

	/** creates a CCAtlasNode  with an Atlas file the width and height of each item and the quantity of items to render*/
	public static CCAtlasNode create(String tile, int tileWidth, int tileHeight, int itemsToRender) {
		CCAtlasNode pRet = new CCAtlasNode();
		if(pRet.initWithTileFile(tile, tileWidth, tileHeight, itemsToRender)) {
			return pRet;
		}
		pRet = null;
		return null;
	}

	/** initializes an CCAtlasNode  with an Atlas file the width and height of each item and the quantity of items to render*/
	public boolean initWithTileFile(String tile, int tileWidth, int tileHeight, int itemsToRender) {
		ccMacros.CCAssert(tile != null, "title should not be null");
		CCTexture2D texture = CCTextureCache.sharedTextureCache().addImage(tile);
		return initWithTexture(texture, tileWidth, tileHeight, itemsToRender);
	}

	/** initializes an CCAtlasNode  with a texture the width and height of each item measured in points and the quantity of items to render*/
	public boolean initWithTexture(CCTexture2D texture, int tileWidth, int tileHeight, int itemsToRender) {
		m_uItemWidth  = tileWidth;
		m_uItemHeight = tileHeight;

		m_tColorUnmodified.set(ccColor3B.ccWHITE);
		m_bIsOpacityModifyRGB = true;

		m_tBlendFunc.set(ccConfig.CC_BLEND_SRC, ccConfig.CC_BLEND_DST);

// TODO		m_pTextureAtlas = new CCTextureAtlas();
// TODO		m_pTextureAtlas.initWithTexture(texture, itemsToRender);
		m_pTextureAtlas = CCTextureAtlas.textureAtlas(texture, itemsToRender);

		if(m_pTextureAtlas == null) {
			ccMacros.CCLOG(TAG, "cocos2d: Could not initialize CCAtlasNode. Invalid Texture.");
			return false;
		}

		this.updateBlendFunc();
		this.updateOpacityModifyRGB();

		this.calculateMaxItems();

		// TODO legacy
		calculateTexCoordsSteps();

		m_uQuadsToDraw = itemsToRender;

		// shader stuff
		setShaderProgram(CCShaderCache.sharedShaderCache().programForKey(CCGLProgram.kCCShader_PositionTexture_uColor));
		m_nUniformColor = GLES20.glGetUniformLocation(getShaderProgram().getProgram(), "u_color");

		return true;
	}

	/** updates the Atlas (indexed vertex array).
	 * Shall be overriden in subclasses
	 */
	public void updateAtlasValues() {
		ccMacros.CCAssert(false, "CCAtlasNode:Abstract updateAtlasValue not overridden");
	}

	@Override
	public void draw() {
		ccMacros.CC_NODE_DRAW_SETUP();

		ccGLStateCache.ccGLBlendFunc(m_tBlendFunc.src, m_tBlendFunc.dst);

		float [] colors = {_displayedColor.r / 255.0f, _displayedColor.g / 255.0f, _displayedColor.b / 255.0f, _displayedOpacity / 255.0f};
		getShaderProgram().setUniformLocationWith4fv(m_nUniformColor, colors, 1);

		m_pTextureAtlas.drawNumberOfQuads(m_uQuadsToDraw, 0);

/* TODO legacy -->
		GL10 gl = CCDirector.gl;

		// 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(GL10.GL_COLOR_ARRAY);

		gl.glColor4f(_displayedColor.r / 255f, _displayedColor.g / 255f, _displayedColor.b / 255f, _displayedOpacity / 255f);

		boolean newBlend = false;
		if (m_tBlendFunc.src != ccConfig.CC_BLEND_SRC || m_tBlendFunc.dst != ccConfig.CC_BLEND_DST) {
			newBlend = true;
			gl.glBlendFunc(m_tBlendFunc.src, m_tBlendFunc.dst);
		}

		m_pTextureAtlas.drawQuads(gl);

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


		// is this chepear than saving/restoring color state ?
		// XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color
		// XXX: that they need
		//    	glColor4ub( 255, 255, 255, 255);

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

	// CC Texture protocol

	/** returns the used texture*/
	public CCTexture2D getTexture() {
		return m_pTextureAtlas.getTexture();
	}

	/** sets a new texture. it will be retained*/
	public void setTexture(CCTexture2D texture) {
		m_pTextureAtlas.setTexture(texture);
		this.updateBlendFunc();
		this.updateOpacityModifyRGB();
	}

	@Override
	public boolean isOpacityModifyRGB() {
		return m_bIsOpacityModifyRGB;
	}

	@Override
	public void setOpacityModifyRGB(boolean isOpacityModifyRGB) {
		ccColor3B oldColor = this.getColor();
		m_bIsOpacityModifyRGB = isOpacityModifyRGB;
		this.setColor(oldColor);
	}

	@Override
	public ccColor3B getColor() {
		if (m_bIsOpacityModifyRGB) {
			return m_tColorUnmodified;
		}
		return super.getColor();
	}

	@Override
	public void setColor(final ccColor3B color) {
		ccColor3B tmp = new ccColor3B(color);
		m_tColorUnmodified.set(color);

		if(m_bIsOpacityModifyRGB) {
			tmp.r = tmp.r * _displayedOpacity/255;
			tmp.g = tmp.g * _displayedOpacity/255;
			tmp.b = tmp.b * _displayedOpacity/255;
		}
		super.setColor(tmp);
	}

	public void setOpacity(int opacity) {
		super.setOpacity(opacity);

		// special opacity for premultiplied textures
		if(m_bIsOpacityModifyRGB) {
			this.setColor(m_tColorUnmodified);
		}
	}

	private void calculateMaxItems() {
		final CCSize s = m_pTextureAtlas.getTexture().getContentSize();
		m_uItemsPerColumn = (int)(s.height / m_uItemHeight);
		m_uItemsPerRow = (int)(s.width / m_uItemWidth);
	}

	public void updateBlendFunc() {
		if(! (m_pTextureAtlas.getTexture().hasPremultipliedAlpha() )) {
			m_tBlendFunc.src = GLES20.GL_SRC_ALPHA;
			m_tBlendFunc.dst = GLES20.GL_ONE_MINUS_SRC_ALPHA;
		}
	}

	public void updateOpacityModifyRGB() {
		m_bIsOpacityModifyRGB = m_pTextureAtlas.getTexture().hasPremultipliedAlpha();
	}


	// TODO legacy -->

    /// texture coordinate x increment
    protected float texStepX;
    /// texture coordinate y increment
    protected float texStepY;

    private void calculateTexCoordsSteps() {
        CCTexture2D tex = m_pTextureAtlas.getTexture();
        texStepX = m_uItemWidth / (float) tex.getPixelsWide();
        texStepY = m_uItemHeight / (float) tex.getPixelsHigh();
    }
}

// end of base_node group
/// @}
