package com.leakedbits.codelabs.box2d;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.Shape;
import com.badlogic.gdx.physics.box2d.World;
import com.leakedbits.codelabs.box2d.utils.Box2DFactory;
import com.leakedbits.codelabs.utils.Sample;

public class CollisionsSample extends Sample implements ContactListener {

	/* Use Box2DDebugRenderer, which is a model renderer for debug purposes */
	private Box2DDebugRenderer debugRenderer;

	/* As always, we need a camera to be able to see the objects */
	private OrthographicCamera camera;

	/* Define a world to hold all bodies and simulate reactions between them */
	private World world;

	private Body walls;

	private boolean ballTouchedWall;
	private boolean ballTouchedBox;

	/* Fields to store previous accelerometer values in each iteration */
	private float prevAccelX;
	private float prevAccelY;

	/**
	 * Main constructor used to update sample name.
	 */
	public CollisionsSample() {
		name = "Collisions";
	}

	@Override
	public void render(float delta) {
		/*
		 * Clear screen with a black background. Use red instead if the ball
		 * touched a wall or blue if touched the box.
		 */
		if (ballTouchedWall) {
			Gdx.gl.glClearColor(0.6f, 0, 0, 1);
		} else if (ballTouchedBox) {
			Gdx.gl.glClearColor(0, 0, 0.6f, 1);
		} else {
			Gdx.gl.glClearColor(0, 0, 0, 1);
		}
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		/* Check if we should change the gravity */
		processAccelerometer();

		/* Render all graphics before do physics step */
		debugRenderer.render(world, camera.combined);

		/* Step the simulation with a fixed time step of 1/60 of a second */
		world.step(1 / 60f, 6, 2);
	}

	@Override
	public void show() {
		/*
		 * This line is found in every sample but is not necessary for the
		 * sample functionality. calls Sample.show() method. That method set the
		 * sample to receive all touch and key input events. Also prevents the
		 * app from be closed whenever the user press back button and instead
		 * returns to main menu.
		 */
		super.show();

		/*
		 * Create world with a common gravity vector (9.81 m/s2 downwards force)
		 * and tell world that we want objects to sleep. This last value
		 * conserves CPU usage. As we use the accelerometer and the world
		 * gravity to change bodies positions, we can't let bodies to sleep.
		 */
		world = new World(new Vector2(0, -9.81f), false);

		/* Create renderer */
		debugRenderer = new Box2DDebugRenderer();

		/*
		 * Define camera viewport. Box2D uses meters internally so the camera
		 * must be defined also in meters. We set a desired width and adjust
		 * height to different resolutions.
		 */
		camera = new OrthographicCamera(20,
				20 * (Gdx.graphics.getHeight() / (float) Gdx.graphics
						.getWidth()));

		/* Create the ball */
		Shape shape = Box2DFactory.createCircleShape(1);
		FixtureDef fixtureDef = Box2DFactory.createFixture(shape, 2.5f, 0.25f,
				0.75f, false);
		Box2DFactory.createBody(world, BodyType.DynamicBody, fixtureDef,
				new Vector2(5, 0));

		/* Create the box */
		shape = Box2DFactory.createBoxShape(0.5f, 0.5f, new Vector2(0, 0), 0);
		fixtureDef = Box2DFactory.createFixture(shape, 1, 0.5f, 0.5f, false);
		Box2DFactory.createBody(world, BodyType.StaticBody, fixtureDef,
				new Vector2(0, 0));

		/* Create the walls */
		walls = Box2DFactory.createWalls(world, camera.viewportWidth,
				camera.viewportHeight, 1);

		world.setContactListener(this);
	}

	@Override
	public void dispose() {
		debugRenderer.dispose();
		world.dispose();
	}

	private void processAccelerometer() {

		/* Get accelerometer values */
		float y = Gdx.input.getAccelerometerY();
		float x = Gdx.input.getAccelerometerX();

		/*
		 * If accelerometer values have changed since previous processing,
		 * change world gravity.
		 */
		if (prevAccelX != x || prevAccelY != y) {

			/* Negative on the x axis but not in the y */
			world.setGravity(new Vector2(y, -x));

			/* Store new accelerometer values */
			prevAccelX = x;
			prevAccelY = y;
		}
	}

	@Override
	public void beginContact(Contact contact) {
		Fixture fixtureA = contact.getFixtureA();
		Fixture fixtureB = contact.getFixtureB();

		/*
		 * If one of the fixture contacting is an static body, and is the wall,
		 * set ballTouchedWall to true.
		 */
		if (fixtureA.getBody().getType() == BodyType.StaticBody) {
			if (fixtureA.getBody().equals(walls)) {
				ballTouchedBox = false;
				ballTouchedWall = true;
			} else {
				ballTouchedBox = true;
				ballTouchedWall = false;
			}
		} else if (fixtureB.getBody().getType() == BodyType.StaticBody) {
			if (fixtureA.getBody().equals(walls)) {
				ballTouchedBox = false;
				ballTouchedWall = true;
			} else {
				ballTouchedBox = true;
				ballTouchedWall = false;
			}
		}
	}

	@Override
	public void endContact(Contact contact) {

	}

	@Override
	public void preSolve(Contact contact, Manifold oldManifold) {

	}

	@Override
	public void postSolve(Contact contact, ContactImpulse impulse) {

	}

}