package j3deditor.bin.editor.tools;

import j3deditor.bin.editor.Scene3D;

import j3deditor.bin.hierarchy.J3DeGeometry;
import j3deditor.bin.hierarchy.J3DeScene;

import j3deditor.bin.hierarchy.J3DeShape3D;

import j3deditor.bin.hierarchy.J3DeTexture;
import j3deditor.bin.hierarchy.util.ProjectLoader;
import j3deditor.bin.hierarchy.util.Shape3DType;

import j3deditor.bin.util.Utilities;

import java.awt.GridLayout;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.media.j3d.Appearance;

import javax.media.j3d.Material;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.Texture;

import javax.media.j3d.TextureAttributes;

import javax.media.j3d.Transform3D;

import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

import javax.vecmath.Color4f;


/**
 * UI for controlling <code>Scene3D</code> properties.
 * 
 * @author Risto Seene
 */
public class ViewPanel3D extends JPanel implements PropertyChangeListener{

	private static Material defaultMaterial = new Material();

	private static PolygonAttributes points = new PolygonAttributes(PolygonAttributes.POLYGON_POINT, PolygonAttributes.CULL_BACK, 0);

	private static PolygonAttributes lines = new PolygonAttributes(PolygonAttributes.POLYGON_LINE, PolygonAttributes.CULL_BACK, 0);

	private static PolygonAttributes filled = new PolygonAttributes(PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_BACK, 0);

	private static Texture texture = null;

	private Scene3D scene3D;

	private J3DeScene scene;

	private J3DeShape3D shape3D;

	private Appearance appearance;

	private Object lock;

	private final String sceneFile = Utilities.threeDData + "scene.j3de";


	private JComboBox drawMode;

	private JComboBox matMode;

	private JCheckBox showTex;


	/**
	 * Creates an instance of <code>ViewPanel3D</code>.
	 */
	public ViewPanel3D(){
		this.lock = new Object();
		this.setOpaque(false);
		initScene();
		initContent();
	}

	/**
	 * Initializes the content of this panel.
	 */
	private void initContent(){
		this.setLayout(new GridLayout(2, 3, 1, 1));

		this.add(new JLabel(Utilities.translator.translate(this.getClass(), "title")));

		for(int i = 0; i < 2; i++){
			this.add(new JLabel());
		}

		this.drawMode = new JComboBox();
		this.drawMode.addItem(Utilities.translator.translate(this.getClass(), "verts"));
		this.drawMode.addItem(Utilities.translator.translate(this.getClass(), "edges"));
		this.drawMode.addItem(Utilities.translator.translate(this.getClass(), "faces"));
		this.drawMode.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					if(drawMode.getSelectedIndex() == 0){
						appearance.setPolygonAttributes(points);
					}
					else if(drawMode.getSelectedIndex() == 1){
						appearance.setPolygonAttributes(lines);
					}
					else if(drawMode.getSelectedIndex() == 2){
						appearance.setPolygonAttributes(filled);
					}
				}
			});
		this.drawMode.setSelectedIndex(2);
		this.add(drawMode);

		this.matMode = new JComboBox();
		this.matMode.addItem(Utilities.translator.translate(this.getClass(), "none"));
		this.matMode.addItem(Utilities.translator.translate(this.getClass(), "shade"));
		this.matMode.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					if(matMode.getSelectedIndex() == 0){
						appearance.setMaterial(null);
					}
					else if(matMode.getSelectedIndex() == 1){
						appearance.setMaterial(defaultMaterial);
					}
				}
			});
		this.matMode.setSelectedIndex(1);
		this.add(matMode);

		this.showTex = new JCheckBox(Utilities.translator.translate(this.getClass(), "tex"), false);
		this.showTex.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					if(showTex.isSelected() && texture != null){
						appearance.setTexture(texture);
					}
					else {
						appearance.setTexture(null);
					}
				}
			});
		this.showTex.setEnabled(false);
		this.showTex.setOpaque(false);
		this.add(showTex);
	}

	/**
	 * Loads default scene from file and adds required objects.
	 */
	private void prepareScene(){
		try{
			synchronized(lock){
				ProjectLoader loader = new ProjectLoader(this.scene);
				loader.addPropertyChangeListener(this);
				loader.loadProject(Utilities.util.getResource(this.sceneFile));
				lock.wait();
			}
		} catch(Throwable t){
			Utilities.out.throwError("Loading scene failed!");
		}

		this.shape3D = new J3DeShape3D(Shape3DType.COLORCUBE);
		this.shape3D.getAppearance().setMaterial(defaultMaterial);
		this.appearance = this.shape3D.getAppearance().getAppearance();
		this.appearance.setPolygonAttributes(filled);
		this.appearance.setTextureAttributes(new TextureAttributes(TextureAttributes.MODULATE, new Transform3D(), new Color4f(0, 0, 0, 0), TextureAttributes.NICEST));
		this.scene.addNewObject(this.shape3D);
	}

	/**
	 * Initializes the scene shown in Scene3D.
	 */
	private void initScene(){
		this.scene = new J3DeScene("");
		prepareScene();

		this.scene3D = new Scene3D(scene);
	}


	/**
	 * Returns <code>Scene3D</code> object created by this panel.
	 * 
	 * @return <code>Scene3D</code> object created by this panel
	 */
	public Scene3D getScene3D(){
		return this.scene3D;
	}

	/**
	 * Returns <code>J3DeScene</code> created by this panel.
	 * 
	 * @return <code>J3DeScene</code> created by this panel
	 */
	public J3DeScene getScene(){
		return this.scene;
	}

	/**
	 * Sets the geometry of the object shown in the <code>Scene3D</code>.
	 * 
	 * @param geometry
	 * 	new geometry
	 */
	public void setGeometry(J3DeGeometry geometry){
		this.shape3D.setGeometry(geometry, 0);
	}

	/**
	 * Sets the texture of the object shown in the <code>Scene3D</code>.
	 * @param texture
	 * 	new texture
	 */
	public void setTexture(J3DeTexture texture){
		if(texture != null){
			ViewPanel3D.texture = texture.getTexture();
			this.showTex.setEnabled(true);
		}
	}

	/**
	 * Sent called by background thread that loads the scene from file.
	 * 
	 * @param e
	 * 	PropertyChangeEvent
	 */
	public void propertyChange(PropertyChangeEvent e){
		if(e.getNewValue() == SwingWorker.StateValue.DONE){
			synchronized(lock){
				lock.notifyAll();
			}
		}
	}

	/**
	 * Disposes all resources.
	 */
	public void cleanUp(){
	          texture = null;
		this.scene3D.cleanUp();
	          this.scene3D = null;
	          this.scene = null;
	          this.shape3D = null;
	          this.appearance = null;
	}
	
}
