/*******************************************************************************
 * Copyright 2013 pyros2097
 * 
 * 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 scene3d.demo;

import scene3d.Actor3d;
import scene3d.Camera3d;
import scene3d.Group3d;
import scene3d.Stage3d;
import scene3d.actions.Actions3d;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.InputListener;

public class Scene3dDemo implements ApplicationListener {
	Stage3d stage3d;
	Stage stage2d;
	Skin skin;
	ModelBuilder modelBuilder;
	CameraInputController camController;
	Model model, model2, model3;
	Actor3d knight, actor2, actor3, floor, skydome;
	Group3d group3d;
	Label fpsLabel;
	Label visibleLabel;
	Label positionLabel;
	Label rotationLabel;
	Label positionCameraLabel;
	Label rotationCameraLabel;
	
	public static void main(String[] argc) {
		LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
		cfg.audioDeviceBufferCount = 20;
		cfg.title = "Stage3d Test";
		cfg.useGL20 = false;
		cfg.width = 852;
		cfg.height = 480;
		new LwjglApplication(new Scene3dDemo(), cfg);
	}
	boolean rightKey, leftKey, upKey, downKey, spaceKey;
    @Override
    public void create () {
    	//2d stuff
    	stage2d = new Stage();
    	skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
    	fpsLabel = new Label("ff", skin);
    	fpsLabel.setPosition(Gdx.graphics.getWidth() - 260, Gdx.graphics.getHeight()-40);
    	visibleLabel = new Label("ff", skin);
    	visibleLabel.setPosition(Gdx.graphics.getWidth() - 260, Gdx.graphics.getHeight()- 60);
    	positionLabel = new Label("Position", skin);
    	positionLabel.setPosition(Gdx.graphics.getWidth() - 260, Gdx.graphics.getHeight()- 80);
    	rotationLabel = new Label("Rotation", skin);
    	rotationLabel.setPosition(Gdx.graphics.getWidth() - 260, Gdx.graphics.getHeight()- 100);
    	positionCameraLabel = new Label("Position", skin);
    	positionCameraLabel.setPosition(20, Gdx.graphics.getHeight()- 40);
    	rotationCameraLabel = new Label("Rotation", skin);
    	rotationCameraLabel.setPosition(20, Gdx.graphics.getHeight()- 60);
    	stage2d.addActor(fpsLabel);
    	stage2d.addActor(visibleLabel);
    	stage2d.addActor(positionLabel);
    	stage2d.addActor(rotationLabel);
    	stage2d.addActor(positionCameraLabel);
    	stage2d.addActor(rotationCameraLabel);
    	stage2d.addListener(new InputListener(){
    		@Override
    		public boolean keyUp(InputEvent event, int keycode) {
    			if (keycode == Keys.LEFT) leftKey = false;
    			if (keycode == Keys.RIGHT) rightKey = false;
    			if (keycode == Keys.UP) upKey = false;
    			if (keycode == Keys.DOWN) downKey = false;
    			if (keycode == Keys.SPACE) spaceKey = false;
    			return super.keyUp(event, keycode);
    		}
    		@Override
    		public boolean keyDown(InputEvent event, int keycode) {
    			if (keycode == Keys.LEFT) leftKey = true;
    			if (keycode == Keys.RIGHT) rightKey = true;
    			if (keycode == Keys.UP) upKey = true;
    			if (keycode == Keys.DOWN) downKey = true;
    			if (keycode == Keys.SPACE) spaceKey = true;
    			return super.keyDown(event, keycode);
    		}
    	});
    	
    	//3dstuff
    	stage3d = new Stage3d();
    	modelBuilder = new ModelBuilder();
    	model = modelBuilder.createBox(5f, 5f, 5f, new Material("Color", ColorAttribute.createDiffuse(Color.WHITE)),
                Usage.Position | Usage.Normal);
		
    	model2 = modelBuilder.createBox(2f, 2f, 2f, new Material("Color", ColorAttribute.createDiffuse(Color.WHITE)),
                Usage.Position | Usage.Normal);
    	actor2 = new Actor3d(model2, 10f, 0f, 0f);
    	model3 = modelBuilder.createBox(2f, 2f, 2f, new Material("Color", ColorAttribute.createDiffuse(Color.ORANGE)),
                Usage.Position | Usage.Normal);
    	actor3 = new Actor3d(model3, -10f, 0f, 0f);
    	actor2.setColor(Color.RED);
    	actor2.setName("actor2");
    	actor3.setName("actor3");
    	camController = new CameraInputController(stage3d.getCamera());
    	InputMultiplexer im = new InputMultiplexer();
    	im.addProcessor(stage2d);// 2d should get click events first
		//im.addProcessor(stage3d);
		im.addProcessor(camController);
		Gdx.input.setInputProcessor(im);
    	stage3d.touchable = Touchable.enabled; // only then will it detect hit actor3d
    	
    	ModelBuilder builder = new ModelBuilder();
		builder.begin();
		MeshPartBuilder part = builder.part("floor", GL20.GL_TRIANGLES, Usage.Position | Usage.TextureCoordinates | Usage.Normal,
			new Material());
		for (float x = -200f; x < 200f; x += 10f) {
			for (float z = -200f; z < 200f; z += 10f) {
				part.rect(x, 0, z + 10f, x + 10f, 0, z + 10f, x + 10f, 0, z, x, 0, z, 0, 1, 0);
			}
		}
		floor = new Actor3d(builder.end());
		AssetManager am = new AssetManager();
    	am.load("data/g3d/knight.g3db", Model.class);
    	am.load("data/g3d/skydome.g3db", Model.class);
    	am.load("data/g3d/concrete.png", Texture.class);
    	am.finishLoading();
    	knight = new Actor3d(am.get("data/g3d/knight.g3db", Model.class), 0f, 18f, 0f);
    	knight.getAnimation().inAction = true;
		knight.getAnimation().animate("Walk", -1, 1f, null, 0.2f);
    	skydome = new Actor3d(am.get("data/g3d/skydome.g3db", Model.class));
    	floor.materials.get(0).set(TextureAttribute.createDiffuse(am.get("data/g3d/concrete.png", 
    			Texture.class)));
    	stage3d.addActor3d(skydome);
		stage3d.addActor3d(floor);
		knight.setPitch(-90f);
		knight.setYaw(-130f);
		testActor3d();
		//stage3d.addAction3d(Actions3d.rotateBy(0f, 90f, 0f, 2f));
    	//testGroup3d();
    	//testStage3d();
    }
    
    float angle, angle2;
    @Override
    public void render () {
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        stage3d.act();
    	stage3d.draw();
    	stage2d.act();
     	stage2d.draw();
    	camController.update();
    	fpsLabel.setText("Fps: " + Gdx.graphics.getFramesPerSecond());
    	visibleLabel.setText("Visible: " + stage3d.getRoot().visibleCount);
    	positionLabel.setText("Knight X:" + knight.getX()+" Y:"+knight.getY()+" Z:"+knight.getZ());
    	rotationLabel.setText("Knight Yaw:" + knight.getYaw()+" Pitch:"+knight.getPitch()+" "
    			+ "Roll:"+knight.getRoll());
    	positionCameraLabel.setText("Camera X:" + stage3d.getCamera().position.x
    			+" Y:"+stage3d.getCamera().position.y+" Z:"+stage3d.getCamera().position.z);
    	rotationCameraLabel.setText("Camera Yaw:" + stage3d.getCamera().direction.x
    			+" Pitch:"+stage3d.getCamera().direction.y+" "+ "Roll:"+stage3d.getCamera().direction.z);
    	
    	angle = MathUtils.cosDeg(knight.getYaw() - 90); //90 degrees is correction factor
    	angle2 = -MathUtils.sinDeg(knight.getYaw() - 90);
		if (upKey) {
			knight.addAction3d(Actions3d.moveBy(angle, 0f, angle2));
			stage3d.getCamera().translate(angle, 0f, angle2);
		} 
		else if (downKey) {
			knight.addAction3d(Actions3d.moveBy(-angle, 0f, -angle2));
			stage3d.getCamera().translate(-angle, 0f, -angle2);
		}
		else if (rightKey) {
			knight.rotateYaw(-2f);
			if(stage3d.getCamera().direction.z > -0.76f)
				Camera3d.rotateBy(-2f, 0f, 0f, 0f);
			//stage3d.getCamera().translate(angle, 0f, angle2); //get the angle calculations rite to make
			// the camera truly follow knight
		} 
		else if (leftKey) {
			knight.rotateYaw(2f);
			if(stage3d.getCamera().direction.z < -0.63f)
				Camera3d.rotateBy(2f, 0f, 0f, 0f);
			
		} 
		/* private float stateTime;
		    float angleDegrees;
		    stateTime+= delta;
			angleDegrees = stateTime * 10.0f;
			camera.position.set(200f * MathUtils.sinDeg(angleDegrees),
				20, -200.0f * MathUtils.cosDeg(angleDegrees));
			camera.rotate(Vector3.Y, angleDegrees/10f*delta);*/
    }

	@Override
	public void resize(int width, int height) {
		stage2d.setViewport(width, height);
		stage3d.setViewport(width, height);
	}
	
	@Override
	public void pause() {}

	@Override
	public void resume() {}
	
    @Override
	public void dispose () {
	   stage3d.dispose();
	}
	
    
	void testActor3d(){
    	stage3d.addActor3d(knight);
    	stage3d.getCamera().position.set(knight.getX()+ 13f, knight.getY() + 24f, knight.getZ() + 45f);
    	//stage3d.getCamera().lookAt(knight.getX(), knight.getY(), knight.getZ());
    	//Camera3d.rotateCameraBy(knight.getYaw(), 0f, 0f, 1f);
    	//Camera3d.followOffset(20f, 20f, -20f);
    	//Camera3d.followActor3d(knight, false);
    	//stage3d.moveBy(-50f, 0f, 0f, 2f);
        //stage3d.addActor3d(actor2);
    	//actor1.addAction3d(Actions3d.rotateTo(60f, 5f));
    	//actor1.addAction3d(Actions3d.scaleBy(0.5f, 0.5f, 0.5f, 5f, Interpolation.linear));
       // actor1.addAction3d(Actions3d.forever(Actions3d.sequence(Actions3d.moveBy(50f, 0f, 0f, 10f),
       // 		Actions3d.moveBy(-50f, 0f, 0f, 10f))));
     //   actor1.addAction3d(Actions3d.sequence(
     //   		Actions3d.moveBy(7f, 0f, 0f, 2f),
      //  		Actions3d.scaleTo(0.5f, 0.5f, 0.5f, 5f),
      //  		Actions3d.moveBy(-7f, 0f, 0f, 2f)));
    	
    	//actor1.addAction3d(Actions3d.scaleTo(1.2f, 1.2f, 1.2f, 1f));
    	//actor2.addAction3d(Actions3d.scaleBy(0.3f, 0.3f, 0.3f, 5f));
       //actor1.addAction3d(Actions3d.sequence(Actions3d.moveBy(7f, 0f, 0f, 2f), Actions3d.moveBy(-7f, 0f, 0f, 2f)));
        //actor1.addAction3d(Actions3d.moveTo(7f, 0f, 0f, 3f));
       // actor1.addAction3d(Actions3d.moveTo(-10f, 0f, 0f, 2f));
        /*
         *  Since both actions are running at same time we get the difference in x
         *  ans : actor.getX() = -3.0000014
         */
        /* if you see this the setRotation method called alone works but after this moveTo is called and the actor1
         * rotation is reset to original .. this is the problem because of transformation matrix
         * and the Sequence action problem is i think because i'm using the pools from gdx.utils.pools
         */
        //actor1.setRotation(59);
        //actor1.setPosition(5f, 0f, 0f);
       // actor2.addAction3d(Actions3d.moveBy(-7f, 0f, 0f, 2f));
       
        // r.setRotation(59);
        //r.addAction3d(Actions.rotateTo(59, 1f));
        //r.addAction3d(Actions.rotateBy(59, 1f));
    }
    
    void testGroup3d(){
    	group3d = new Group3d();
    	group3d.setPosition(0f, 0f, 0f);
    	group3d.setName("group1");
    	stage3d.addActor3d(group3d);
    	group3d.addActor3d(knight);
    	group3d.addActor3d(actor2);
    	group3d.addActor3d(actor3);
    	group3d.addAction3d(Actions3d.sequence(Actions3d.moveTo(0f, 5f, 0f, 2f), 
    			Actions3d.moveTo(5f, 0f, 0f, 2f), Actions3d.scaleTo(0f, 5f, 0f, 2f)));
    }
    
    void testStage3d(){
    	stage3d.addActor3d(knight);
        stage3d.addActor3d(actor2);
    	stage3d.addAction3d(Actions3d.moveTo(-7f, 0f, 0f, 3f));
    }
}