/**
 * O3D Sample
 */
var g_simple,
	g_cube,
	g_client,
	g_o3d,
	g_math,
	g_cubeWidth = 10,
	g_cubeMax = g_cubeWidth * g_cubeWidth * g_cubeWidth,
	g_cubeSize = 3,
	g_camera = {
		farPlane: 5000,
		nearPlane: 0.4
	},
	g_quaternions,
	g_pack,
	g_o3dElement,
	g_thisRot,
	g_lastRot,
	g_aball,
	g_o3dWidth,
	g_o3dHeight,
	g_dragging = false,
	g_viewInfo,
	g_sampler = [],
	g_eye,
	g_target,
	g_up,
	g_cubeTransform,
	g_cubeTransformChild = [],
	g_clock = 0,
	g_clock_child = 0,
	g_timeMult = 1,
	g_finished = false;

var Cube = Cube || {} ;

Cube.init = function() {
	o3djs.util.makeClients(Cube.setup);
};
Cube.uninit = function(){
	if (g_client) {
		g_client.cleanup();
	}
};
Cube.wheelEvent = function(e){
	g_eye = g_math.mul((e.deltaY < 0 ? 11 : 13) / 12, g_eye);
	g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_eye, g_target, g_up);
};
Cube.createTexture = function(_url){
	var url = _url ? _url : "http://blog.xlune.com/2009/04/assets/texture.jpg" ;
	o3djs.io.loadTexture(g_pack, url, function(texture, exception) {
		Cube.dropTexture();
		for(var i in g_sampler){
			if (exception) {
				g_sampler[i].texture = null;
				g_textureLoadDenied = true;
			} else {
				g_sampler[i].texture = texture;
				g_cubeTransform.parent = g_client.root;
				$('#texture').data('toggle', true);
				g_finished = true;
			}
		}
		
	});
};
Cube.dropTexture = function(){
	if (g_sampler.texture) {
		for(var i in g_sampler){
			g_pack.removeObject(g_sampler[i].texture);
		}
	}
	$('#texture').data('toggle', false);
};
Cube.startDragging = function startDragging(e) {
	g_lastRot = g_thisRot;
	g_aball.click([e.x, e.y]);
	g_dragging = true;
};
Cube.drag = function drag(e) {
	if(g_dragging) {
		var rotationQuat = g_aball.drag([e.x, e.y]);
		var rot_mat = g_quaternions.quaternionToRotation(rotationQuat);
		g_thisRot = g_math.mul(g_lastRot, rot_mat);
		
		var m = g_client.root.localMatrix;
		g_math.matrix4.setUpper3x3(m, g_thisRot);
		g_client.root.localMatrix = m;
	}
};
Cube.stopDragging = function(e) {
	g_dragging = false;
};
Cube.setClientSize = function () {
	var newWidth  = parseInt(g_client.width);
	var newHeight = parseInt(g_client.height);

	if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
		g_o3dWidth = newWidth;
		g_o3dHeight = newHeight;

		Cube.updateProjection();

		// Sets a new area size for arcball.
		g_aball.setAreaSize(g_o3dWidth, g_o3dHeight);
	}
};
Cube.updateProjection = function(){
	g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
		g_math.degToRad(45), g_o3dWidth / g_o3dHeight, g_camera.nearPlane,
		g_camera.farPlane
	);
};
Cube.create = function(material, count){
	// Create a Shape object for the mesh.
	var cubeShape = g_pack.createObject('Shape');

	// Create the Primitive that will contain the geometry data for
	// the cube.
	var cubePrimitive = g_pack.createObject('Primitive');

	// Create a StreamBank to hold the streams of vertex data.
	var streamBank = g_pack.createObject('StreamBank');

	// Assign the material that was passed in to the primitive.
	cubePrimitive.material = material;

	// Assign the Primitive to the Shape.
	cubePrimitive.owner = cubeShape;

	// Assign the StreamBank to the Primitive.
	cubePrimitive.streamBank = streamBank;

	// The cube is made of 12 triangles. There's eight vertices in total which
	// are shared between the face triangles.
	cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;
	cubePrimitive.numberPrimitives = 12; // 12 triangles
	cubePrimitive.numberVertices = 8;		// 8 vertices in total

	// Generate the draw element for the cube primitive.
	cubePrimitive.createDrawElement(g_pack, null);

	// Create a javascript array that stores the X, Y and Z coordinates of each
	// of the 24 vertices used by the cube.
	var positionArray = [
		 -0.5, -0.5, 0.5,
		0.5, -0.5, 0.5,
		0.5, 0.5, 0.5,
		-0.5, 0.5, 0.5,
		-0.5, 0.5, 0.5,
		0.5, 0.5, 0.5,
		0.5, 0.5, -0.5,
		-0.5, 0.5, -0.5,
		-0.5, 0.5, -0.5,
		0.5, 0.5, -0.5,
		0.5, -0.5, -0.5,
		-0.5, -0.5, -0.5,
		-0.5, -0.5, -0.5,
		0.5, -0.5, -0.5,
		0.5, -0.5, 0.5,
		-0.5, -0.5,	0.5,
		0.5, -0.5, 0.5,
		0.5, -0.5, -0.5,
		0.5, 0.5, -0.5,
		0.5, 0.5, 0.5,
		-0.5, -0.5, -0.5,
		-0.5, -0.5, 0.5,
		-0.5, 0.5, 0.5,
		-0.5, 0.5, -0.5
	];
	
	var posMargin = g_cubeWidth/2 * g_cubeSize;
	
	for(var i in positionArray){
		switch(true){
			case i%3==0:
				//x
				positionArray[i] += count%g_cubeWidth * g_cubeSize - posMargin;
				break;
			case i%3==1:
				//y
				positionArray[i] += Math.floor(count%(g_cubeWidth*g_cubeWidth)/g_cubeWidth) * g_cubeSize - posMargin;
				break;
			case i%3==2:
				//z
				positionArray[i] += Math.floor(count/(g_cubeWidth*g_cubeWidth)) * g_cubeSize - posMargin;
				break;
		}
	}

	// The following array stores the texture coordinates (u, v) for each vertex.
	// These coordinates are used by the shader when displaying the texture image
	// on the mesh triangles.
	var texCoordsArray = [
		0, 0,
		1, 0,
		1, 1,
		0, 1,
		0, 0,
		1, 0,
		1, 1,
		0, 1,
		1, 1,
		0, 1,
		0, 0,
		1, 0,
		0, 0,
		1, 0,
		1, 1,
		0, 1,
		0, 0,
		1, 0,
		1, 1,
		0, 1,
		0, 0,
		1, 0,
		1, 1,
		0, 1
	];

	// The following array defines how vertices are to be put together to form
	// the triangles that make up the cube's faces.	In the index array, every
	// three elements define a triangle.	So for example vertices 0, 1 and 2
	// make up the first triangle, vertices 0, 2 and 3 the second one, etc.
	var indicesArray = [
		0, 1, 2,
		0, 2, 3,
		4, 5, 6,
		4, 6, 7,
		8, 9, 10,
		8, 10, 11,
		12, 13, 14,
		12, 14, 15,
		16, 17, 18,
		16, 18, 19,
		20, 21, 22,
		20, 22, 23
	];

	// Create buffers containing the vertex data.
	var positionsBuffer = g_pack.createObject('VertexBuffer');
	var positionsField = positionsBuffer.createField('FloatField', 3);
	positionsBuffer.set(positionArray);

	var texCoordsBuffer = g_pack.createObject('VertexBuffer');
	var texCoordsField = texCoordsBuffer.createField('FloatField', 2);
	texCoordsBuffer.set(texCoordsArray);


	var indexBuffer = g_pack.createObject('IndexBuffer');
	indexBuffer.set(indicesArray);
	// Associate the positions Buffer with the StreamBank.
	streamBank.setVertexStream(
		g_o3d.Stream.POSITION,	// semantic: This stream stores vertex positions
		0,						// semantic index: First (and only) position stream
		positionsField,			// field: the field this stream uses.
		0						// start_index: How many elements to skip in the field.
	);
	
	// Associate the texture coordinates buffer with the primitive.
	streamBank.setVertexStream(
			g_o3d.Stream.TEXCOORD,	// semantic
			0,						// semantic index
			texCoordsField,			// field
			0						// start_index
	);
	
	// Associate the triangle indices Buffer with the primitive.
	cubePrimitive.indexBuffer = indexBuffer;
	return cubeShape;
};
Cube.callBack = function(renderEvent){
	if(!g_dragging){
		g_clock += renderEvent.elapsedTime * g_timeMult;
		g_cubeTransform.identity();
		g_cubeTransform.rotateX(0.01 * g_clock);
		g_cubeTransform.rotateY(0.1 * g_clock);
		g_cubeTransform.rotateZ(0.01 * g_clock);
	}
	
	Cube.setClientSize();

};

Cube.setup = function(clientElements){
	g_o3dElement = clientElements[0];
	
	g_client = g_o3dElement.client;
	g_o3d = g_o3dElement.o3d;
	g_math = o3djs.math;
	g_quaternions = o3djs.quaternions;
	g_pack = g_client.createPack();
	
	g_viewInfo = o3djs.rendergraph.createBasicView(
		g_pack,
		g_client.root,
		g_client.renderGraphRoot
	);
	
	g_lastRot = g_thisRot = g_math.matrix4.identity();
	g_aball = o3djs.arcball.create(100, 100);
	
	/**
	 * Setting Up the Draw Context
	 *
	 */
	g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
		g_math.degToRad(30),
		g_client.width / g_client.height,
		1,
		5000);

	// Set up our view transformation to look towards the world origin where the
	// cube is located.
	g_eye = [0, 4, 0];
	g_target = [0, 0, 0];
	g_up = [0, 0, -1];
	g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
		g_eye,
		g_target,
		g_up
	);
	
	/**
	 * Creating an Effect and Loading the Shaders
	 *
	 */
	var customEffect = g_pack.createObject('Effect');
	// looks in the HTML document for an element named "effect"
	var shaderString = document.getElementById('effect').value;
	// loads the entire contents of the <textarea id="effect"> element into the customEffect object
	customEffect.loadFromFXString(shaderString);
	
	/**
	 * Creating the Material and Shape
	 *
	 */
	var cubeShape = [];
	for(var i=0; i<g_cubeMax; i++){
		var simpleMaterial = g_pack.createObject('Material');
		simpleMaterial.drawList = g_viewInfo.performanceDrawList;
		simpleMaterial.effect = customEffect;
		
		customEffect.createUniformParameters(simpleMaterial);
		
		var samplerParam = simpleMaterial.getParam('texSampler0');
		g_sampler[i] = g_pack.createObject('Sampler');
		g_sampler[i].minFilter = g_o3d.Sampler.ANISOTROPIC;
		g_sampler[i].maxAnisotropy = 4;
		samplerParam.value = g_sampler[i];
		
		cubeShape[i] = Cube.create(simpleMaterial, i);
	}
	
	/**
	 * Setting Up the Transform Graph
	 *
	 */
	g_cubeTransform = g_pack.createObject('Transform');
	g_cubeTransform.parent = g_client.root;
	for(var i in cubeShape){
		g_cubeTransform.addShape(cubeShape[i]);
	}
	Cube.setClientSize();
	
	
	/**
	 * g_client.setRenderCallback(renderCallback);
	 *
	 */
	g_client.setRenderCallback(Cube.callBack);
	
	
	/**
	 * Event
	 *
	 */
	o3djs.event.addEventListener(g_o3dElement, 'mousedown', Cube.startDragging);
	o3djs.event.addEventListener(g_o3dElement, 'mousemove', Cube.drag);
	o3djs.event.addEventListener(g_o3dElement, 'mouseup', Cube.stopDragging);
	o3djs.event.addEventListener(g_o3dElement, 'wheel', Cube.wheelEvent);
	
	
	/**
	 * Texture
	 *
	 */
	Cube.createTexture();

};

(function(){
	//o3djs.basePath = "http://blog.xlune.com/2009/04/o3djs";
	o3djs.require('o3djs.util');
	o3djs.require('o3djs.math');
	o3djs.require('o3djs.rendergraph');
	o3djs.require('o3djs.io');
	o3djs.require('o3djs.arcball');
	o3djs.require('o3djs.quaternions');
	
	$(window)
		.load(Cube.init)
		.unload(Cube.uninit);

})();
