package com.google.ar.core.examples.java.common.framework.view3D;

import com.google.ar.core.Camera;
import com.google.ar.core.examples.java.common.framework.model3D.BackgroundBox;
import com.google.ar.core.examples.java.common.framework.model3D.IViewer3D;
import com.google.ar.core.examples.java.common.framework.model3D.Position3D;
import com.google.ar.core.examples.java.common.java3d.Box;
import com.google.ar.core.examples.java.common.java3d.Cone;
import com.google.ar.core.examples.java.common.java3d.Cylinder;
import com.google.ar.core.examples.java.common.java3d.GraphicsContext3D;
import com.google.ar.core.examples.java.common.java3d.Light;
import com.google.ar.core.examples.java.common.java3d.Node;
import com.google.ar.core.examples.java.common.java3d.Shape3D;
import com.google.ar.core.examples.java.common.java3d.Sphere;
import com.google.ar.core.examples.java.common.java3d.Transform3D;
import com.google.ar.core.examples.java.common.java3d.Vector3d;

import java.util.ArrayList;

public class Viewer3D implements IViewer3D {
	private GraphicsContext3D gc3D = null;
	private ArrayList<Light> lights = null;
	private BackgroundBox skyBox = null;
	private int width;
	private int height;
	private Camera camera = null;

	public Viewer3D(GraphicsContext3D gc3D) {
		this.gc3D = gc3D;
	}

	@Override
	public void surfaceChanged(int width, int height) {
		this.width =width;
		this.height = height;
		if (camera != null) {
			float xfocalLength = camera.getImageIntrinsics().getFocalLength()[0];
			float yfocalLength = camera.getImageIntrinsics().getFocalLength()[1];
//			gc3D.update(width, height, (float)camera.getFieldOfView(), (float)camera.getFrontClipDistance(), (float)camera.getBackClipDistance(), false);
			float w = (float)camera.getImageIntrinsics().getImageDimensions()[1];
			float h = (float)camera.getImageIntrinsics().getImageDimensions()[0];
//			gc3D.update(this.width, this.height, (float) Math.atan(w / 2.0f / xfocalLength) * 2.0f, 0.1f, 100.0f, false);
			gc3D.update(this.width, this.height, h, yfocalLength, 0.1f, 100.0f, false);
		}
	}


	@Override
	public void onDrawFrame() {
		gc3D.onDrawFrame(width, height);
	}

	@Override
	public void updateCamera(Camera camera) {
		// カメラの更新
		this.camera = camera;
		if (camera != null) {
			float viewPoint[] = camera.getDisplayOrientedPose().getTranslation();
			float viewLine[] = camera.getDisplayOrientedPose().getZAxis();
			float viewUp[] = camera.getDisplayOrientedPose().getYAxis();
			float xfocalLength = camera.getImageIntrinsics().getFocalLength()[0];
			float yfocalLength = camera.getImageIntrinsics().getFocalLength()[1];
			Position3D eye = new Position3D(viewPoint[0], viewPoint[1], viewPoint[2]);
			Position3D center = eye.clone().add(new Vector3d(-viewLine[0], -viewLine[1], -viewLine[2]));
			Vector3d up = new Vector3d(viewUp[0], viewUp[1] * camera.getPose().qy(), viewUp[2]);
			float w = (float)camera.getImageIntrinsics().getImageDimensions()[1];
			float h = (float)camera.getImageIntrinsics().getImageDimensions()[0];
			gc3D.update(this.width, this.height, h, yfocalLength, 0.1f, 100.0f, eye, center, up, false);
		}
	}

	@Override
	public void update(ArrayList<Light> lights, BackgroundBox skyBox) {
		// 光源の更新
		if (this.lights != lights) {
			this.lights = lights;
		}

		// スカイボックスの更新
		if (this.skyBox != skyBox) {
			this.skyBox = skyBox;
		}
	}

	@Override
	public void draw(Node node) {
		if (node instanceof Box) {
			gc3D.draw(((Box)node).getShape(Box.FRONT));
			gc3D.draw(((Box)node).getShape(Box.BACK));
			gc3D.draw(((Box)node).getShape(Box.LEFT));
			gc3D.draw(((Box)node).getShape(Box.RIGHT));
			gc3D.draw(((Box)node).getShape(Box.TOP));
			gc3D.draw(((Box)node).getShape(Box.BOTTOM));
		} else if (node instanceof Cone) {
			gc3D.draw(((Cone)node).getShape(Cone.BODY));
			gc3D.draw(((Cone)node).getShape(Cone.CAP));
		} else if (node instanceof Cylinder) {
			gc3D.draw(((Cylinder)node).getShape(Cylinder.BODY));
			gc3D.draw(((Cylinder)node).getShape(Cylinder.TOP));
			gc3D.draw(((Cylinder)node).getShape(Cylinder.BOTTOM));
		} else if (node instanceof Sphere) {
			gc3D.draw(((Sphere)node).getShape(Sphere.BODY));
		} else if (node instanceof Shape3D) {
			gc3D.draw((Shape3D)node);
		} else if (node instanceof Light){
			gc3D.updateLightState((Light)node);
		}
	}

	@Override
	public void pushTransform(Transform3D transform) {
		gc3D.pushMatrix();
		gc3D.multMatrix(transform);
	}

	@Override
	public void popTransform() {
		gc3D.popMatrix();
	}
}
