package org.ntlab.radishforandroidstudio.framework.model3D;

import org.ntlab.radishforandroidstudio.framework.physics.Ground;
import org.ntlab.radishforandroidstudio.java3d.BranchGroup;
import org.ntlab.radishforandroidstudio.java3d.Group;
import org.ntlab.radishforandroidstudio.java3d.Leaf;
import org.ntlab.radishforandroidstudio.java3d.Light;
import org.ntlab.radishforandroidstudio.java3d.Node;
import org.ntlab.radishforandroidstudio.java3d.Primitive;
import org.ntlab.radishforandroidstudio.java3d.Transform3D;
import org.ntlab.radishforandroidstudio.java3d.TransformGroup;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class Universe {
	private BranchGroup root = null;
	private ArrayList<Light> lights = new ArrayList<Light>();
	private BackgroundBox skyBox = null;
	private Ground ground = null;
	private ArrayList<Movable> movableList = new ArrayList<Movable>();
    private Map<Object, Object3D> objMap = new HashMap<>();
	
	public Universe() {
		root = new BranchGroup();
	}
	
	public void render(IViewer3D viewer) {
		viewer.update(lights, skyBox);
		render(viewer, root);
	}
	
	private void render(IViewer3D viewer, Node node) {
		if (node instanceof Group) {
			if (node instanceof Primitive) {
				viewer.draw((Primitive) node);
			} else {
				if (node instanceof TransformGroup) {
					Transform3D transform = new Transform3D();
					((TransformGroup) node).getTransform(transform);
					//
//					Vector3d vec = new Vector3d(1.0, 1.1, 1.0);
//					transform.setScale(vec);
//					AxisAngle4d aa = new AxisAngle4d(0.0, 0.0, 1.0, 1.0);
//					transform.setRotation(aa);
					//
					viewer.pushTransform(transform);
					for (int i = 0; i < ((Group) node).numChildren(); i++) {
						Node node2 = ((Group) node).getChild(i);
						render(viewer, node2);
					}
					viewer.popTransform();
				} else {
					for (int i = 0; i < ((Group) node).numChildren(); i++) {
						Node node2 = ((Group) node).getChild(i);
						render(viewer, node2);
					}
				}
			}
		} else if (node instanceof Leaf) {
			viewer.draw(node);
		}
	}

	public boolean doHaveObj(Object key) {
		return objMap.get(key) != null;
	}

	public void apply(HashMap<Object, ArrayList<Property3D>> properties) {
		for (Map.Entry<Object, ArrayList<Property3D>> entry : properties.entrySet()) {
			Object3D target = objMap.get(entry.getKey());
			for (Property3D prop : entry.getValue()) {
				target.apply(prop, false);
			}
		}
	}

	/**
	 * Idを持つ対象に対して座標等の更新をかける
	 *
	 * @param id
	 * @param properties
	 * @return boolean 存在しないなどで更新できなければfalse, できればtrue
	 */
	public boolean apply(Object id, ArrayList<Property3D> properties) {
		Object3D target = objMap.get(id);
		if (target == null) return false;
		for (Property3D prop : properties) {
			target.apply(prop, false);
		}
		return true;
	}

	public void update(long interval){
		for(int i = 0; i < movableList.size(); i++){
			Movable movable = movableList.get(i);
			movable.motion(interval,ground);			
		}
	}

	/**
	 * オブジェクトを配置する
	 * 
	 * @param obj
	 *            配置するオブジェクト
	 */
	public void place(Placeable obj) {
		if(obj instanceof Ground){
			ground = (Ground)obj;
		}
		if(obj instanceof Movable){
			movableList.add((Movable)obj);
		}			
		place(obj.getTransformGroupToPlace());
	}

	public void place(Node node) {
		root.addChild(node);
	}

	public void place(Object id, Object3D obj) {
		objMap.put(id, obj);
	}

	/**
	 * 後で取り除けるようにオブジェクトを配置する
	 * 
	 * @param obj
	 *            配置するオブジェクト
	 */
	public void placeDisplacable(Placeable obj) {
		placeDisplacable(obj.getTransformGroupToPlace());
	}

	private void placeDisplacable(Node node) {
		root.addChild(node);
	}

	/**
	 * 光源の追加
	 * 
	 * @param light
	 *            追加する光源
	 */
	public void placeLight(Light light) {
		root.addChild(light);
		getLights().add(light);
	}
	
	/**
	 * スカイボックスの追加
	 * @param skyBox 追加するスカイボックス
	 */
	public void placeSkyBox(BackgroundBox skyBox) {
		root.addChild(skyBox);		
		this.skyBox = skyBox;
	}

	/**
	 * オブジェクトを可能ならば取り除く
	 * 
	 * @param obj
	 *            取り除くオブジェクト
	 */
	public void displace(Placeable obj) {
		displace(obj.getTransformGroupToPlace());
	}

	private void displace(Node node) {
		root.removeChild(node);
	}

	public void displace(Object id) {
		Object3D t;
		if ((t = objMap.get(id)) != null) {
			displace(t);
			objMap.remove(id);
		}
	}

	public ArrayList<Light> getLights() {
		return lights;
	}

}
