package j3deditor.bin.hierarchy;


import j3deditor.bin.hierarchy.util.HNodeType;
import j3deditor.bin.hierarchy.util.LightType;

import j3deditor.bin.util.Utilities;

import java.awt.Color;

import javax.media.j3d.AmbientLight;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Light;
import javax.media.j3d.Node;
import javax.media.j3d.PointLight;
import javax.media.j3d.SpotLight;

import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;


/**
 * Contains and manages an instance of
 * <code>javax.media.j3d.Light</code>.
 *
 * @author Risto Seene
 * 
 * @see javax.media.j3d.Light
 */
public class J3DeLight extends J3DeObject{

	/** actual Java 3D <code>Light</code> object */
	private Light object;

	/** number of <code>J3DeLight</code> nodes */
	private static long numOfNodes = 0;


	/**
	 * Creates an empty <code>J3DeLight</code> object.
	 * Requires initialization with <code>init3DObject()</code> method
	 */
	public J3DeLight(){
		super("Light", HNodeType.LIGHT, numOfNodes);
		numOfNodes++;
	}


	/**
	 * Creates an instance of <code>J3DeLight</code> with the given type.
	 *
	 * @param type
	 *	the type of the light
	 */
	public J3DeLight(LightType type){
		super("Light" + "[" + numOfNodes + "]_" + type, HNodeType.LIGHT, numOfNodes);
		numOfNodes++;

		if(type == LightType.AMBIENTLIGHT){
			object = new AmbientLight();
			initObject(object);
		} else if(type == LightType.DIRECTIONALLIGHT){
			object = new DirectionalLight();
			initObject(object);
		} else if(type == LightType.POINTLIGHT){
			object = new PointLight();
			initObject(object);

		} else if(type == LightType.SPOTLIGHT){
			object = new SpotLight();
			initObject(object);
		}

		object.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0), 100));

		setCapabilities();
	}


	/**
	 * Sets the capabilities for the <code>Light</code>
	 * this node controls.
	 *
	 * <br><ul><li><code>Light.ALLOW_COLOR_READ</code></li>
	 * <li><code>Light.ALLOW_COLOR_WRITE</code></li>
	 * <li><code>Light.ALLOW_INFLUENCING_BOUNDS_READ</code></li>
	 * <li><code>Light.ALLOW_INFLUENCING_BOUNDS_WRITE</code></li>
	 * <li><code>Light.ALLOW_SCOPE_READ</code></li>
	 * <li><code>Light.ALLOW_SCOPE_WRITE</code></li>
	 * <li><code>Light.ALLOW_STATE_READ</code></li>
	 * <li><code>Light.ALLOW_STATE_WRITE</code></li></ul>
	 * <ul><li><code>PointLight.ALLOW_ATTENUATION_READ</code></li>
	 * <li><code>PointLight.ALLOW_ATTENUATION_WRITE</code></li></ul>
	 * <ul><li><code>SpotLight.ALLOW_CONCENTRATION_READ</code></li>
	 * <li><code>SpotLight.ALLOW_CONCENTRATION_WRITE</code></li>
	 * <li><code>SpotLight.ALLOW_SPREAD_ANGLE_READ</code></li>
	 * <li><code>SpotLight.ALLOW_SPREAD_ANGLE_WRITE</code></li></ul>
	 *
	 * @see javax.media.j3d.Light#setCapability(int capability)
	 * @see javax.media.j3d.PointLight#setCapability(int capability)
	 * @see javax.media.j3d.SpotLight#setCapability(int capability)
	 */
	private void setCapabilities(){
		object.setCapability(Light.ALLOW_COLOR_READ);
		object.setCapability(Light.ALLOW_COLOR_WRITE);
		object.setCapability(Light.ALLOW_INFLUENCING_BOUNDS_READ);
		object.setCapability(Light.ALLOW_INFLUENCING_BOUNDS_WRITE);
		object.setCapability(Light.ALLOW_SCOPE_READ);
		object.setCapability(Light.ALLOW_SCOPE_WRITE);
		object.setCapability(Light.ALLOW_STATE_READ);
		object.setCapability(Light.ALLOW_STATE_WRITE);

		if(object instanceof PointLight){
			object.setCapability(PointLight.ALLOW_ATTENUATION_READ);
			object.setCapability(PointLight.ALLOW_ATTENUATION_WRITE);
		}
		if(object instanceof SpotLight){
			object.setCapability(SpotLight.ALLOW_CONCENTRATION_READ);
			object.setCapability(SpotLight.ALLOW_CONCENTRATION_WRITE);
			object.setCapability(SpotLight.ALLOW_SPREAD_ANGLE_READ);
			object.setCapability(SpotLight.ALLOW_SPREAD_ANGLE_WRITE);
		}
	}


	/**
	 * Applies the given <code>Node</code> object to this node.
	 *
	 * @param obj
	 *	object to be applied to this node
	 */
	public void init3DObject(Node obj){
		object = (Light)obj;
		initObject(object);
	}


	/**
	 * Clones this node.
	 *
	 * @param node
	 *	object sent by subclass, otherwise <code>null</code>
	 * @return Returns the clone of this node
	 */
	public HierarchyNode clone(HierarchyNode node){
		if(node == null){
			node = new J3DeLight();
		}

		J3DeLight light = (J3DeLight)super.clone(node);
		Light object = (Light)light.get3DObject();
		object.setInfluencingBounds(new BoundingSphere(object.getInfluencingBounds()));
		light.init3DObject(object);

		return light;
	}


	/**
	 * Returns the type of this light.
	 *
	 * @return Returns the type of this light
	 */
	public int getLightType(){
		if(object instanceof SpotLight){
			return 3;
		} else if(object instanceof PointLight){
			return 2;
		} else if(object instanceof DirectionalLight){
			return 1;
		} else if(object instanceof AmbientLight){
			return 0;
		}
		return -1;
	}


	/**
	 * Tests if the <code>Light</code> object is enabled.
	 *
	 * @return Returns <code>true</code> if <code>Light</code> is enabled,
	 *	otherwise <code>false</code>
	 *
	 * @see javax.media.j3d.Light#getEnable()
	 */
	public boolean getEnable(){
		return object.getEnable();
	}


	/**
	 * Passes the given state to the <code>Light</code> object.
	 *
	 * @param state
	 *	<code>true</code> if light is enabled,
	 *	otherwise <code>false</code>
	 *
	 * @see javax.media.j3d.Light#setEnable(boolean state)
	 */
	public void setEnable(boolean state){
		object.setEnable(state);
	}


	/**
	 * Returns the color of the <code>Light</code> object.
	 *
	 * @return Returns the color of this light
	 *
	 * @see javax.media.j3d.Light#getColor(Color3f color)
	 */
	public Color getColor(){
		Color3f color = new Color3f();
		object.getColor(color);

		return color.get();
	}


	/**
	 * Sets the color of this light.
	 *
	 * @param color
	 *	new color
	 *
	 * @see javax.media.j3d.Light#setColor(Color3f color)
	 */
	public void setColor(Color color){
		if(color != null){
			object.setColor(new Color3f(color));
		}
	}


	/**
	 * Returns the attenuation values of the <code>Light</code> object
	 * if this is pointlight.
	 *
	 * @return Returns a 3 element array of attenuation values,
	 *	if this is not pointlight all values are 0
	 *
	 * @see javax.media.j3d.PointLight#getAttenuation(Point3f a)
	 */
	public int[] getAttenuation(){
		int[] at = new int[3];
		if(object instanceof PointLight){
			Point3f point = new Point3f();
			((PointLight)object).getAttenuation(point);
			at[0] = (int)point.getX();
			at[1] = (int)point.getY();
			at[2] = (int)point.getZ();
			return at;
		}

		at[0] = 0;
		at[1] = 0;
		at[2] = 0;
		return at;
	}


	/**
	 * Sets the attenuation values of the <code>Light</code> object
	 * if this is pointlight.
	 *
	 * @param constant
	 *	value of constant attenuation
	 * @param linear
	 *	value of linear attenuation
	 * @param quadratic
	 *	value of quadratic attenuation
	 *
	 * @see javax.media.j3d.PointLight#setAttenuation(float c, float l, float q)
	 */
	public void setAttenuation(int constant, int linear, int quadratic){
		if(object instanceof PointLight){
			((PointLight)object).setAttenuation(constant, linear, quadratic);
		}
	}


	/**
	 * Returns the concentration value of this <code>Light</code> object
	 * if this is spotlight.
	 *
	 * @return Returns the concentration value,
	 *	if this is not spotlight returns 0
	 *
	 * @see javax.media.j3d.SpotLight#getConcentration()
	 */
	public int getConcentration(){
		if(object instanceof SpotLight){
			return 10 * (int)(((SpotLight)object).getConcentration());
		}
		return 0;
	}


	/**
	 * Sets the concentration value of the <code>Light</code> object
	 * if this is spotlight.
	 *
	 * @param conc
	 *	value of concentration
	 *
	 * @see javax.media.j3d.SpotLight#setConcentration(float c)
	 */
	public void setConcentration(int conc){
		if(object instanceof SpotLight){
			((SpotLight)object).setConcentration(((float)conc / 10));
		}
	}


	/**
	 * Returns the spread angle of this <code>Light</code> object
	 * if this is spotlight.
	 *
	 * @return Returns the spread angle,
	 *	if this is not spotlight returns 0
	 *
	 * @see javax.media.j3d.SpotLight#getSpreadAngle()
	 */
	public int getSpreadAngle(){
		if(object instanceof SpotLight){
			return (int)(360 * ((SpotLight)object).getSpreadAngle() / Math.PI);
		}
		return 0;
	}


	/**
	 * Sets the spread angle of the <code>Light</code> object
	 * if this is spotlight.
	 *
	 * @param spread
	 *	spread angle
	 *
	 * @see javax.media.j3d.SpotLight#setSpreadAngle(float a)
	 */
	public void setSpreadAngle(int spread){
		if(object instanceof SpotLight){
			((SpotLight)object).setSpreadAngle((float)(Math.PI * spread / 360));
		}
	}


	/**
	 * Returns the radius of <code>BoundingSphere</code>.
	 *
	 * @return Returns the radius of <code>BoundingSphere</code>
	 */
	public float getBoundsRadius(){
		return (float)(((BoundingSphere)object.getInfluencingBounds()).getRadius());
	}


	/**
	 * Sets the radius of <code>BoundingSphere</code>.
	 *
	 * @param radius
	 *	new radius
	 */
	public void setBoundsRadius(float radius){
		try{
			BoundingSphere bounds = ((BoundingSphere)object.getInfluencingBounds());
			bounds.setRadius(radius);
			object.setInfluencingBounds(bounds);
		} catch(Throwable t){
			Utilities.out.throwWarning(t);
		}
	}


	/**
	 * Returns the coordinates of the center of <code>BoundingSphere</code>.
	 *
	 * @return Returns the center coordinates as an array
	 */
	public float[] getBoundsPosition(){
		float[] pos = new float[3];
		Point3d point = new Point3d();
		((BoundingSphere)object.getInfluencingBounds()).getCenter(point);
		pos[0] = (float)(point.getX());
		pos[1] = (float)(point.getY());
		pos[2] = (float)(point.getZ());

		return pos;
	}


	/**
	 * Sets the coordinates of the center of <code>BoundingSphere</code>.
	 *
	 * @param x
	 *	coordinate on X-axis
	 * @param y
	 *	coordinate on Y-axis
	 * @param z
	 *	coordinate on Z-axis
	 */
	public void setBoundsPosition(float x, float y, float z){
		try{
			BoundingSphere bounds = ((BoundingSphere)object.getInfluencingBounds());
			bounds.setCenter(new Point3d(x, y, z));
			object.setInfluencingBounds(bounds);
		} catch(Throwable t){
			Utilities.out.throwWarning(t);
		}
	}

}
