package org.cocos2d.grid;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import org.cocos2d.opengl.CCTexture2D;
import org.cocos2d.types.CCVertex3D;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.CGSize;
import org.cocos2d.types.ccQuad3;

/**
 * @addtogroup effects
 * @{
 */

/**
 * CCGrid3D is a 3D grid implementation. Each vertex has 3 dimensions: x,y,z
 */
public class CCGrid3D extends CCGridBase {

	public CCGrid3D() {
	}

	/** returns the vertex at a given position */
	public CCVertex3D vertex(final CGPoint pos) {
		assert pos.x == (int)pos.x && pos.y == (int)pos.y : "Numbers must be integers";

		int index = (int) ((pos.x * (m_sGridSize.height + 1) + pos.y) * 3);
		CCVertex3D vert = new CCVertex3D(m_pVertices.get(index + 0), m_pVertices.get(index + 1), m_pVertices.get(index + 2));

		return vert;
	}

	/** returns the original (non-transformed) vertex at a given position */
	public CCVertex3D originalVertex(final CGPoint pos) {
		int index = (int) ((pos.x * (m_sGridSize.height + 1) + pos.y) * 3);
		CCVertex3D vert = new CCVertex3D(m_pOriginalVertices.get(index + 0), m_pOriginalVertices.get(index + 1), m_pOriginalVertices.get(index + 2));

		return vert;
	}

	/** sets a new vertex at a given position */
	public void setVertex(final CGPoint pos, CCVertex3D vertex) {
		assert pos.x == (int)pos.x && pos.y == (int)pos.y : "Numbers must be integers";
		int index = (int) ((pos.x * (m_sGridSize.height + 1) + pos.y) * 3);
		m_pVertices.put(index + 0, vertex.x);
		m_pVertices.put(index + 1, vertex.y);
		m_pVertices.put(index + 2, vertex.z);
	}

	@Override
	public void blit(GL10 gl) {
		int n = (int) (m_sGridSize.width * m_sGridSize.height);

		// 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);

		ByteBuffer vbb = ByteBuffer.allocateDirect(m_pVertices.limit()*3*4);
		//System.out.printf("vertices limit = %d\n", vertices.limit());
		vbb.order(ByteOrder.nativeOrder());
		mVertexBuffer = vbb.asFloatBuffer();
		mVertexBuffer.clear();
		mVertexBuffer.position(0);
		for (int i = 0; i < m_pVertices.limit(); i=i+3) {
			mVertexBuffer.put(m_pVertices.get(i));
			mVertexBuffer.put(m_pVertices.get(i+1));
			mVertexBuffer.put(m_pVertices.get(i+2));
		}
		mVertexBuffer.position(0);
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
		// gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, m_pTexCoordinates);
		m_pIndices.position(0);

		gl.glDrawElements(GL10.GL_TRIANGLES, n * 6, GL10.GL_UNSIGNED_SHORT, m_pIndices);

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

	@Override
	public void reuse(GL10 gl) {
		if (m_nReuseGrid > 0) {
//            memcpy(originalVertices, vertices, (getGridWidth()+1)*(getGridHeight()+1)*sizeof(CCVertex3D));
			m_nReuseGrid--;
		}
	}

	@Override
	public void calculateVertexPoints() {
		float width = (float)m_pTexture.pixelsWide();
		float height = (float)m_pTexture.pixelsHigh();
		// float imageH = texture_.getContentSize().height;

		int x, y, i;

		ByteBuffer vfb = ByteBuffer.allocateDirect((int) (ccQuad3.size * (m_sGridSize.width + 1) * (m_sGridSize.height + 1) * 4));
		vfb.order(ByteOrder.nativeOrder());
		m_pVertices = vfb.asFloatBuffer();
		// vertices = BufferProvider.createFloatBuffer(ccQuad3.size * (gridSize_.x + 1) * (gridSize_.y + 1));

		ByteBuffer ofb = ByteBuffer.allocateDirect((int) (ccQuad3.size * (m_sGridSize.width + 1) * (m_sGridSize.height + 1) * 4));
		ofb.order(ByteOrder.nativeOrder());
		m_pOriginalVertices = ofb.asFloatBuffer();
		// originalVertices = BufferProvider.createFloatBuffer(ccQuad3.size * (gridSize_.x + 1) * (gridSize_.y + 1));

		ByteBuffer tfb = ByteBuffer.allocateDirect((int) (2 * (m_sGridSize.width + 1) * (m_sGridSize.height + 1) * 4));
		tfb.order(ByteOrder.nativeOrder());
		m_pTexCoordinates = tfb.asFloatBuffer();
		// texCoordinates = BufferProvider.createFloatBuffer(2 * (gridSize_.x + 1) * (gridSize_.y + 1));

		ByteBuffer isb = ByteBuffer.allocateDirect((int) (6 * (m_sGridSize.width + 1) * (m_sGridSize.height + 1) * 2));
		isb.order(ByteOrder.nativeOrder());
		m_pIndices = isb.asShortBuffer();
		// indices = BufferProvider.createShortBuffer(6 * (gridSize_.x + 1) * (gridSize_.y + 1));

		for (y = 0; y < (m_sGridSize.height + 1); y++) {
			for (x = 0; x < (m_sGridSize.width + 1); x++) {
				int idx = (int) ((y * (m_sGridSize.width + 1)) + x);

				m_pVertices.put(idx * 3 + 0, -1);
				m_pVertices.put(idx * 3 + 1, -1);
				m_pVertices.put(idx * 3 + 2, -1);
				m_pVertices.put(idx * 2 + 0, -1);
				m_pVertices.put(idx * 2 + 1, -1);
			}
		}
		m_pVertices.position(0);

		for (x = 0; x < m_sGridSize.width; x++) {
			for (y = 0; y < m_sGridSize.height; y++) {
				int idx = (int) ((y * m_sGridSize.width) + x);

				float x1 = x * m_obStep.x;
				float x2 = x1 + m_obStep.x;
				float y1 = y * m_obStep.y;
				float y2 = y1 + m_obStep.y;

				short a = (short) (x * (getGridHeight() + 1) + y);
				short b = (short) ((x + 1) * (getGridHeight() + 1) + y);
				short c = (short) ((x + 1) * (getGridHeight() + 1) + (y + 1));
				short d = (short) (x * (getGridHeight() + 1) + (y + 1));

				short[] tempidx = {a, b, d, b, c, d};

				m_pIndices.position(6 * idx);
				m_pIndices.put(tempidx, 0, 6);

				int[] l1 = {a * 3, b * 3, c * 3, d * 3};
				CCVertex3D e = new CCVertex3D(x1, y1, 0);
				CCVertex3D f = new CCVertex3D(x2, y1, 0);
				CCVertex3D g = new CCVertex3D(x2, y2, 0);
				CCVertex3D h = new CCVertex3D(x1, y2, 0);

				CCVertex3D[] l2 = {e, f, g, h};

				int[] tex1 = {a * 2, b * 2, c * 2, d * 2};
				CGPoint[] tex2 = {CGPoint.ccp(x1, y1), CGPoint.ccp(x2, y1), CGPoint.ccp(x2, y2), CGPoint.ccp(x1, y2)};

				for (i = 0; i < 4; i++) {
					m_pVertices.put(l1[i] + 0, l2[i].x);
					m_pVertices.put(l1[i] + 1, l2[i].y);
					m_pVertices.put(l1[i] + 2, l2[i].z);

					m_pTexCoordinates.put(tex1[i] + 0, tex2[i].x / width);
					m_pTexCoordinates.put(tex1[i] + 1, tex2[i].y / height);
				}
			}
		}
		m_pIndices.position(0);
		m_pVertices.position(0);
		m_pTexCoordinates.position(0);

		m_pOriginalVertices.put(m_pVertices);
		m_pOriginalVertices.position(0);
	}

	/** create one Grid */
	public static CCGrid3D create(final CGSize gridSize, CCTexture2D pTexture, boolean bFlipped) {
		CCGrid3D pRet= new CCGrid3D();

		if(pRet != null) {
			if(pRet.initWithSize(gridSize, pTexture, bFlipped)) {
			} else {
				pRet = null;
			}
		}

		return pRet;
	}

	/** create one Grid */
	public static CCGrid3D create(final CGSize gridSize) {
		CCGrid3D pRet= new CCGrid3D();

		if(pRet != null) {
			if(pRet.initWithSize(gridSize)) {
			} else {
				pRet = null;
			}
		}

		return pRet;
	}

	protected FloatBuffer m_pTexCoordinates;
	protected FloatBuffer m_pVertices;
	protected FloatBuffer m_pOriginalVertices;
	protected ShortBuffer m_pIndices;

	// TODO legacy -->

    protected FloatBuffer mVertexBuffer;

    public CCGrid3D(CGSize gSize) {
        super(gSize);
        calculateVertexPoints();
    }

}

// end of effects group
/// @}
