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

import com.google.ar.core.examples.java.common.framework.model3D.Object3D;
import com.google.ar.core.examples.java.common.framework.model3D.ObjectVisitor;
import com.google.ar.core.examples.java.common.framework.model3D.Position3D;
import com.google.ar.core.examples.java.common.framework.model3D.Property3D;
import com.google.ar.core.examples.java.common.framework.model3D.Quaternion3D;
import com.google.ar.core.examples.java.common.java3d.Appearance;
import com.google.ar.core.examples.java.common.java3d.Node;
import com.google.ar.core.examples.java.common.java3d.Primitive;
import com.google.ar.core.examples.java.common.java3d.Shape3D;
import com.google.ar.core.examples.java.common.java3d.Texture;
import com.google.ar.core.examples.java.common.java3d.TextureAttributes;
import com.google.ar.core.examples.java.common.java3d.TextureUnitState;
import com.google.ar.core.examples.java.common.java3d.Transform3D;
import com.google.ar.core.examples.java.common.java3d.Vector3d;

import java.util.ArrayList;

/**
 * 階層化されたオブジェクトの姿勢を指定する（各部の位置、向き、テクスチャを指定可能）
 * @author 新田直也
 *
 */
public class Pose3D extends Property3D {
	private ArrayList<PartPose> partPoseList = new ArrayList<PartPose>();

	public Pose3D() {
	}

	public Pose3D(Pose3D p) {
		partPoseList = new ArrayList(p.partPoseList);
	}

	public void applyTo(Object3D obj) {
		// obj.rotate(part, vx, vy, vz, a);
		int n = partPoseList.size();
		for (int i = 0; i < n; i++) {
			final PartPose partpose = (PartPose) partPoseList.get(i);
			Object3D partObj = obj.getPart(partpose.name);
			if (partObj != null) {
				// 位置
				if (partpose.position != null)
					partObj.apply(partpose.position, false);
				// 向き
				if (partpose.quaternion != null)
					partObj.apply(partpose.quaternion, false);
				// テクスチャ
				if (partpose.texture != null 
						|| partpose.texturePosition != null 
						|| partpose.textureQuaternion != null) {
					ObjectVisitor v = new ObjectVisitor() {
						@Override
						public void postVisit(Object3D obj) {
							Appearance appearance = null;
							Node node = obj.getPrimitiveNode();
							if (node != null && node instanceof Shape3D) {
								appearance = ((Shape3D)node).getAppearance();
							} else if (node != null && node instanceof Primitive) {
								appearance = ((Primitive)node).getAppearance();								
							}
							if (appearance != null) {
								TextureUnitState tu = null;
								if (partpose.texture != null) {
									if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) {
										appearance.setTexture(partpose.texture);
									} else {
										tu = appearance.getTextureUnitState(partpose.textureUnit);
										tu.setTexture(partpose.texture);
										appearance.setTextureUnitState(partpose.textureUnit, tu);
									}
								}
								if ((partpose.texturePosition != null || partpose.textureQuaternion != null) 
										&& obj.hasAppearancePrepared()) {	// Appearance が初回のレンダリングの直前で更新される可能性があるため
									TextureAttributes ta;
									if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) {
										ta = appearance.getTextureAttributes();
									} else {
										tu = appearance.getTextureUnitState(partpose.textureUnit);
										ta = tu.getTextureAttributes();
									}
									if (ta == null) {
										ta = new TextureAttributes();
									}
									Transform3D t = new Transform3D();
									if (partpose.texturePosition != null) {
										t.set(new Vector3d(partpose.texturePosition.getVector3d()));
									}
									if (partpose.textureQuaternion != null) {
										Transform3D t2 = new Transform3D();
										t2.set(partpose.textureQuaternion.getQuat());
										t.mul(t2);
									}
//									shape.getAppearance().getTextureAttributes().setTextureTransform(t);
									ta.setTextureTransform(t);
									if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) {
										appearance.setTextureAttributes(ta);
									} else {
										tu.setTextureAttributes(ta);
										appearance.setTextureUnitState(partpose.textureUnit, tu);
									}
								}
							}
						}
						@Override
						public void preVisit(Object3D obj) {
						}
					};
					partObj.accept(v);
				}
			}
		}
	}

	public void addPose(String name, Position3D p, Quaternion3D q, Texture texture, Position3D tp, Quaternion3D tq, int textureUnit) {
		PartPose partpose = new PartPose(name, p, q, texture, tp, tq, textureUnit);
		partPoseList.add(partpose);
	}

	@Override
	public Property3D clone() {
		// TODO Auto-generated method stub
		return new Pose3D(this);
	}
}
