/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.tests.g3d;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.DirectionalLightsAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.PointLightsAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.environment.PointLight;
import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader;
import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader.Config;
import com.badlogic.gdx.graphics.g3d.utils.DefaultShaderProvider;
import com.badlogic.gdx.graphics.profiling.GLProfiler;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.StringBuilder;

/** @author Daniel Holderbaum */
public class Benchmark3DTest extends BaseG3dHudTest {

	protected Environment environment;

	protected Label vertexCountLabel, textureBindsLabel, shaderSwitchesLabel, drawCallsLabel, glCallsLabel, lightsLabel;

	protected CheckBox lightingCheckBox, lightsCheckBox;

	protected boolean lighting;

	private GLProfiler glProfiler;

	@Override
	public void create () {
		super.create();

		glProfiler = new GLProfiler(Gdx.graphics);
		glProfiler.enable();

		randomizeLights();

		cam.position.set(10, 10, 10);
		cam.lookAt(0, 0, 0);
		cam.update();
		showAxes = true;
		lighting = true;

		vertexCountLabel = new Label("Vertices: 999", skin);
		vertexCountLabel.setPosition(0, fpsLabel.getTop());
		hud.addActor(vertexCountLabel);

		textureBindsLabel = new Label("Texture bindings: 999", skin);
		textureBindsLabel.setPosition(0, vertexCountLabel.getTop());
		hud.addActor(textureBindsLabel);

		shaderSwitchesLabel = new Label("Shader switches: 999", skin);
		shaderSwitchesLabel.setPosition(0, textureBindsLabel.getTop());
		hud.addActor(shaderSwitchesLabel);

		drawCallsLabel = new Label("Draw calls: 999", skin);
		drawCallsLabel.setPosition(0, shaderSwitchesLabel.getTop());
		hud.addActor(drawCallsLabel);

		glCallsLabel = new Label("GL calls: 999", skin);
		glCallsLabel.setPosition(0, drawCallsLabel.getTop());
		hud.addActor(glCallsLabel);

		lightsLabel = new Label("Lights: 999", skin);
		lightsLabel.setPosition(0, glCallsLabel.getTop());
		hud.addActor(lightsLabel);

		lightingCheckBox = new CheckBox("Lighting", skin);
		lightingCheckBox.setChecked(lighting);
		lightingCheckBox.addListener(new ChangeListener() {
			@Override
			public void changed (ChangeEvent event, Actor actor) {
				lighting = lightingCheckBox.isChecked();
			}
		});
		lightingCheckBox.setPosition(hudWidth - lightingCheckBox.getWidth(), gridCheckBox.getTop());
		hud.addActor(lightingCheckBox);

		lightsCheckBox = new CheckBox("Randomize lights", skin);
		lightsCheckBox.setChecked(false);
		lightsCheckBox.addListener(new ChangeListener() {
			@Override
			public void changed (ChangeEvent event, Actor actor) {
				lightsCheckBox.setChecked(false);
				randomizeLights();
			}
		});
		lightsCheckBox.setPosition(hudWidth - lightsCheckBox.getWidth(), lightingCheckBox.getTop());
		hud.addActor(lightsCheckBox);

		moveCheckBox.remove();
		rotateCheckBox.remove();
	}

	protected void randomizeLights () {
		int pointLights = MathUtils.random(5);
		int directionalLights = MathUtils.random(5);

		DefaultShader.Config config = new Config();
		config.numDirectionalLights = directionalLights;
		config.numPointLights = pointLights;
		config.numSpotLights = 0;

		modelBatch.dispose();
		modelBatch = new ModelBatch(new DefaultShaderProvider(config));

		environment = new Environment();
		environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1.f));

		for (int i = 0; i < pointLights; i++) {
			environment.add(new PointLight().set(randomColor(), randomPosition(), MathUtils.random(10f)));
		}

		for (int i = 0; i < directionalLights; i++) {
			environment.add(new DirectionalLight().set(randomColor(), randomPosition()));
		}
	}

	protected Color randomColor () {
		return new Color(MathUtils.random(1.0f), MathUtils.random(1.0f), MathUtils.random(1.0f), MathUtils.random(1.0f));
	}

	protected Vector3 randomPosition () {
		return new Vector3(MathUtils.random(-10f, 10f), MathUtils.random(-10f, 10f), MathUtils.random(-10f, 10f));
	}

	private final Vector3 tmpV = new Vector3();
	private final Quaternion tmpQ = new Quaternion();
	private final BoundingBox bounds = new BoundingBox();

	protected void getStatus (final StringBuilder stringBuilder) {
		stringBuilder.setLength(0);
		stringBuilder.append("GL calls: ");
		stringBuilder.append(glProfiler.getCalls());
		glCallsLabel.setText(stringBuilder);

		stringBuilder.setLength(0);
		stringBuilder.append("Draw calls: ");
		stringBuilder.append(glProfiler.getDrawCalls());
		drawCallsLabel.setText(stringBuilder);

		stringBuilder.setLength(0);
		stringBuilder.append("Shader switches: ");
		stringBuilder.append(glProfiler.getShaderSwitches());
		shaderSwitchesLabel.setText(stringBuilder);

		stringBuilder.setLength(0);
		stringBuilder.append("Texture bindings: ");
		stringBuilder.append(glProfiler.getTextureBindings());
		textureBindsLabel.setText(stringBuilder);

		stringBuilder.setLength(0);
		stringBuilder.append("Vertices: ");
		stringBuilder.append(glProfiler.getVertexCount().total);
		vertexCountLabel.setText(stringBuilder);

		DirectionalLightsAttribute dirLights = (DirectionalLightsAttribute)environment.get(DirectionalLightsAttribute.Type);
		PointLightsAttribute pointLights = (PointLightsAttribute)environment.get(PointLightsAttribute.Type);

		stringBuilder.setLength(0);
		stringBuilder.append("Lights: ");

		stringBuilder.append((dirLights == null ? 0 : dirLights.lights.size) + (pointLights == null ? 0 : pointLights.lights.size));
		stringBuilder.append(", Directional: ");
		stringBuilder.append(dirLights == null ? 0 : dirLights.lights.size);
		stringBuilder.append(", Point: ");
		stringBuilder.append(pointLights == null ? 0 : pointLights.lights.size);
		lightsLabel.setText(stringBuilder);

		glProfiler.reset();

		stringBuilder.setLength(0);
		super.getStatus(stringBuilder);
	}

	@Override
	protected void render (ModelBatch batch, Array<ModelInstance> instances) {
		if (lighting) {
			batch.render(instances, environment);
		} else {
			batch.render(instances);
		}
	}

	protected String currentlyLoading;

	@Override
	protected void onModelClicked (final String name) {
		if (name == null) return;

		currentlyLoading = "data/" + name;
		assets.load(currentlyLoading, Model.class);
		loading = true;
	}

	@Override
	protected void onLoaded () {
		if (currentlyLoading == null || currentlyLoading.length() == 0) return;

		final ModelInstance instance = new ModelInstance(assets.get(currentlyLoading, Model.class));
		instance.transform = new Matrix4().idt();
		instance.transform.setToTranslation(MathUtils.random(-10, 10), MathUtils.random(-10, 10), MathUtils.random(-10, 10));
		instance.transform.rotate(Vector3.X, MathUtils.random(-180, 180));
		instance.transform.rotate(Vector3.Y, MathUtils.random(-180, 180));
		instance.transform.rotate(Vector3.Z, MathUtils.random(-180, 180));
		instances.add(instance);
	}

	@Override
	public boolean keyUp (int keycode) {
		if (keycode == Keys.SPACE || keycode == Keys.MENU) {
			onLoaded();
		}
		return super.keyUp(keycode);
	}

	@Override
	public boolean touchUp (int screenX, int screenY, int pointer, int button) {
		onModelClicked(models[MathUtils.random(models.length - 1)]);
		return false;
	}

	@Override
	public void dispose () {
		super.dispose();
		glProfiler.disable();
	}

}
