package com.jackamikaz.testlibgdx;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.vecmath.Vector3f;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.GL11;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.Texture.TextureWrap;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.g3d.loaders.obj.ObjLoader;
import com.badlogic.gdx.math.Vector3;
import com.bulletphysics.collision.broadphase.BroadphaseInterface;
import com.bulletphysics.collision.broadphase.DbvtBroadphase;
import com.bulletphysics.collision.dispatch.CollisionConfiguration;
import com.bulletphysics.collision.dispatch.CollisionDispatcher;
import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration;
import com.bulletphysics.collision.shapes.BoxShape;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.collision.shapes.SphereShape;
import com.bulletphysics.collision.shapes.StaticPlaneShape;
import com.bulletphysics.dynamics.DiscreteDynamicsWorld;
import com.bulletphysics.dynamics.DynamicsWorld;
import com.bulletphysics.dynamics.RigidBody;
import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver;
import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
import com.bulletphysics.linearmath.DefaultMotionState;
import com.bulletphysics.linearmath.Transform;

public class Testlibgdx implements ApplicationListener {
	private PerspectiveCamera camera;
	private Mesh cube;
	private Mesh sphere;
	private Mesh ground;
	private Texture twood;
	private Texture tmetal;
	private Texture tground;
	
	DynamicsWorld physicsWorld;
	
	float[] tempMatrix = new float[16];
	
	BodyDraw[] cubes;
	List<BodyDraw> spheres;
	
	boolean leftButtonWasPressed = false;
	
	@Override
	public void create() {		
		loadStuff();
		initPhysics();
		spheres = new ArrayList<BodyDraw>();
	}

	private void loadStuff() {
		float w = Gdx.graphics.getWidth();
		float h = Gdx.graphics.getHeight();
		
		camera = new PerspectiveCamera(70.0f,w,h);
		camera.position.set(0.0f, 4.0f, 10.0f);
		camera.lookAt(0.0f,5.0f,0.0f);
		
		twood = new Texture(Gdx.files.internal("data/crate12.jpg"));
		twood.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		
		tmetal = new Texture(Gdx.files.internal("data/crate11.jpg"));
		tmetal.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		
		tground = new Texture(Gdx.files.internal("data/metal091.jpg"),true);
		tground.setFilter(TextureFilter.MipMapLinearLinear, TextureFilter.Linear);
		tground.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
		
		try
		{
			InputStream in = Gdx.files.internal("data/uvmappedcube.obj").read();
			cube = ObjLoader.loadObj(in);
			in.close();
			
			in = Gdx.files.internal("data/tinysphere.obj").read();
			sphere = ObjLoader.loadObj(in);
			in.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
		createGround();
	}

	@Override
	public void dispose() {
		cube.dispose();
		sphere.dispose();
		ground.dispose();
		twood.dispose();
		tmetal.dispose();
		tground.dispose();
		
		physicsWorld.destroy();
	}

	@Override
	public void render() {		
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		Gdx.gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);
		Gdx.gl.glEnable(GL10.GL_DEPTH_TEST);
		
		float delta = Gdx.graphics.getDeltaTime();
		
		handleInputForCamera(delta);
		camera.update();
		camera.apply(Gdx.gl10);
		
		boolean leftButtonIsPressed = Gdx.input.isButtonPressed(Buttons.LEFT);
		
		if(!leftButtonWasPressed && leftButtonIsPressed)
			shootSphere();
		
		leftButtonWasPressed = leftButtonIsPressed;
		
		physicsWorld.stepSimulation(delta);
		
		Gdx.gl.glEnable(GL11.GL_TEXTURE_2D);
		
		tground.bind();
		ground.render(GL10.GL_TRIANGLES);
		
		for(BodyDraw c : cubes) {
			c.draw(tempMatrix);
		}
		
		Gdx.gl.glDisable(GL11.GL_TEXTURE_2D);
		for(BodyDraw s : spheres) {
			s.draw(tempMatrix);
		}
	}

	private void handleInputForCamera(float delta) {
		float camspeed = delta * 5.0f;
		
		Vector3 forward = new Vector3(camera.direction);
		forward.y = 0.0f;
		forward.nor().mul(camspeed);
		
		Vector3 strafe = new Vector3(forward);
		strafe.crs(Vector3.Y).nor();
		
		if (Gdx.input.isButtonPressed(Buttons.RIGHT))
		{
			float rotspeed = delta * 30.0f;
			camera.rotate(Vector3.Y,Gdx.input.getDeltaX()*rotspeed);
			camera.rotate(strafe,Gdx.input.getDeltaY()*rotspeed);
			camera.up.set(Vector3.Y);
		}
		
		strafe.mul(camspeed);
		
		if (Gdx.input.isKeyPressed(Keys.DPAD_LEFT)) camera.position.sub(strafe);
		if (Gdx.input.isKeyPressed(Keys.DPAD_RIGHT)) camera.position.add(strafe);
		
		if (Gdx.input.isKeyPressed(Keys.DPAD_UP)) camera.position.add(forward);
		if (Gdx.input.isKeyPressed(Keys.DPAD_DOWN)) camera.position.sub(forward);
	}
	
	private void createGround()
	{
		ground = new Mesh(true,4,6,VertexAttribute.Position(),VertexAttribute.Normal(),VertexAttribute.TexCoords(0));
		
		float halfsize = 20.0f;
		float vertices[] = new float[] {
			-halfsize,0.0f,-halfsize,		0.0f,1.0f,0.0f,		-halfsize,-halfsize,
			 halfsize,0.0f,-halfsize,		0.0f,1.0f,0.0f,		 halfsize,-halfsize,
			 halfsize,0.0f, halfsize,		0.0f,1.0f,0.0f,		 halfsize, halfsize,
			-halfsize,0.0f, halfsize,		0.0f,1.0f,0.0f,		-halfsize, halfsize
		};
		
		short indices[] = new short[] {0,1,2,0,2,3};
		
		ground.setIndices(indices);
		ground.setVertices(vertices);
	}
	
	private void initPhysics()
	{
		// world setup
		BroadphaseInterface broadphase = new DbvtBroadphase();
		CollisionConfiguration config = new DefaultCollisionConfiguration();
		CollisionDispatcher dispatcher = new CollisionDispatcher(config);
		ConstraintSolver solver = new SequentialImpulseConstraintSolver();
		physicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, config);
		
		physicsWorld.setGravity(new Vector3f(0.0f,-10.0f,0.0f));
		
		// ground
		CollisionShape colGround = new StaticPlaneShape(new Vector3f(0.0f,1.0f,0.0f), 0.0f);
		
		RigidBody groundRigidBody = new RigidBody(0.0f, null, colGround);
		physicsWorld.addRigidBody(groundRigidBody);
		
		// cubes
		CollisionShape colCube = new BoxShape(new Vector3f(1.0f,1.0f,1.0f));
		Transform tr = new Transform();
		DefaultMotionState cubeMotionState = null;
		Vector3f inertia = new Vector3f();
		float mass = 0.0f;
		
		cubes = new BodyDraw[21];
		
		// light wooden
		mass = 1.0f;
		colCube.calculateLocalInertia(mass, inertia);
		tr.origin.y = 1.0f;
		for (int j=0; j<3; ++j)
		{
			tr.origin.x = -6.0f;
			for (int i=0; i<5; ++i)
			{
				tr.origin.x += 2.0f;
				cubeMotionState = new DefaultMotionState(tr);
				physicsWorld.addRigidBody(new RigidBody(mass,cubeMotionState,colCube,inertia));
				cubes[i+5*j] = new BodyDraw(cubeMotionState,cube,twood);
			}
			tr.origin.y += 2.0f;
		}
		
		// heavy metal
		mass = 5.0f;
		colCube.calculateLocalInertia(mass, inertia);
		tr.origin.x = -6.0f;
		for (int j=0; j<2; ++j)
		{
			tr.origin.y = 1.0f;
			for (int i=0; i<3; ++i)
			{
				cubeMotionState = new DefaultMotionState(tr);
				physicsWorld.addRigidBody(new RigidBody(mass,cubeMotionState,colCube,inertia));
				cubes[15+i+3*j] = new BodyDraw(cubeMotionState,cube,tmetal);
				tr.origin.y += 2.0f;
			}
			tr.origin.x += 12.0f;
		}
	}
	
	private void shootSphere()
	{
		if (spheres.size() >= 20)
		{
			physicsWorld.removeRigidBody(spheres.get(0).body);
			spheres.remove(0);
		}
		
		Transform tr = new Transform();
		tr.origin.x = camera.position.x;
		tr.origin.y = camera.position.y;
		tr.origin.z = camera.position.z;
		DefaultMotionState motion = new DefaultMotionState(tr);
		
		CollisionShape colSphere = new SphereShape(0.25f);
		Vector3f inertia = new Vector3f();
		float mass = 0.2f;
		colSphere.calculateLocalInertia(mass, inertia);
		
		RigidBody body = new RigidBody(mass,motion,colSphere,inertia);
		Vector3f impulse = new Vector3f(camera.direction.x,camera.direction.y,camera.direction.z);
		impulse.scale(5.0f);
		body.applyCentralImpulse(impulse);
		physicsWorld.addRigidBody(body);
		BodyDraw bd = new BodyDraw(motion,sphere,null);
		bd.body = body;
		spheres.add(bd);
	}

	@Override
	public void resize(int width, int height) {
	}

	@Override
	public void pause() {
	}

	@Override
	public void resume() {
	}
}
