package j3deditor.bin.editor;

import j3deditor.bin.components.ButtonPanelLayout;
import j3deditor.bin.components.ComponentUtils;
import j3deditor.bin.components.TransparentPanel;

import j3deditor.bin.editor.tools.IndexDialog;
import j3deditor.bin.editor.tools.ViewPanel2D;
import j3deditor.bin.editor.tools.ViewPanel3D;
import j3deditor.bin.editor.util.GELayout;
import j3deditor.bin.editor.util.GeometryEditorListener;

import j3deditor.bin.hierarchy.GeometryManipulator;
import j3deditor.bin.hierarchy.J3DeGeometry;
import j3deditor.bin.hierarchy.J3DeGeometryArray;
import j3deditor.bin.hierarchy.J3DeShape3D;

import j3deditor.bin.util.Utilities;

import java.awt.Color;

import javax.media.j3d.Geometry;
import javax.media.j3d.GeometryArray;

import javax.swing.JLabel;


/**
 * Panel that contains the 3D scene and
 * various scene-editor components.
 *
 * @author Risto Seene
 */
public class GeometryEditor extends TransparentPanel implements GeometryEditorListener{


	/** the object this editor manipulates */
	private J3DeShape3D object;

	private int geometryIndex;

	private J3DeGeometry geom;

	private GeometryManipulator gm;

	private Scene2D sceneXY;

	private Scene2D sceneZY;

	private Scene2D sceneXZ;

	private Scene2D sceneTEX;

	private ViewPanel3D panel3D;

	private GeometryEditorListener l;


	/**
	 * Creates an instance of <code>GeometryEditor</code>.
	 *
	 * @param object
	 *	root node of the scene this editor manipulates
	 */
	public GeometryEditor(J3DeShape3D object){
		super(2, 8, Color.white, 0.4f);
		this.object = object;
		setOpaque(false);

		initGeometry();
	}

	/**
	 * Finds the right geometry from the specified Shape3D object
	 */
	private void initGeometry(){
		Geometry[] g = object.getGeometries();
		if(g.length > 0){
			int index = 0;
			if(g.length > 1){
				String question = Utilities.translator.translate(this.getClass(), "chooseGeom");
				index = IndexDialog.askIndex(question, g.length);
			}
			if(g[index] instanceof GeometryArray){
				this.geom = new J3DeGeometryArray((GeometryArray)g[index]);
				this.geometryIndex = index;
				initContent((J3DeGeometryArray)geom);
			}
		}
	}

	/**
	 * Initializes the content of this panel.
	 * 
	 * @param geom
	 * 	<code>J3DeGeometryArray</code> this geometry editor manipulates
	 */
	private void initContent(J3DeGeometryArray geom){
		gm = new GeometryManipulator(geom, GeometryManipulator.VERT_COORDS);
		this.panel3D = new ViewPanel3D();
		this.panel3D.setGeometry(geom);
		this.panel3D.setTexture(this.object.getAppearance().getTexture());

		setLayout(new GELayout(70, 50, 2, 2));

		sceneXY = new Scene2D(Axes2D.POS_Z);
		sceneZY = new Scene2D(Axes2D.POS_X);
		sceneXZ = new Scene2D(Axes2D.POS_Y);
		Scene3D scene3D = panel3D.getScene3D();


		add(new Axes2D(sceneXY));
		add(new ViewPanel2D(this.gm, this));
		add(new Axes2D(sceneZY));
		add(new CameraPanel(sceneXY, 70, 70, 0, 0, ButtonPanelLayout.POSITIVE));
		add(sceneXY);
		add(sceneZY);
		add(new CameraPanel(sceneZY, 70, 70, 0, 0, ButtonPanelLayout.POSITIVE));
		add(new CameraPanel(sceneXZ, 70, 70, 0, 0, ButtonPanelLayout.NEGATIVE));
		add(sceneXZ);
		add(scene3D);
		add(new CameraPanel(panel3D.getScene(), 70, 70, 0, 0, ButtonPanelLayout.NEGATIVE));
		add(new Axes2D(sceneXZ));
		add(panel3D);
		add(new JLabel());

		initSceneSources();
		sceneXY.setGeometryEditorListener(this);
		sceneZY.setGeometryEditorListener(this);
		sceneXZ.setGeometryEditorListener(this);
	}

	/**
	 * Applies the default <code>GeometryManipulator</code>
	 * to all <code>Scene2D</code> objects.
	 */
	private void initSceneSources(){
		sceneXY.setGeometryManipulator(gm);
		sceneZY.setGeometryManipulator(gm);
		sceneXZ.setGeometryManipulator(gm);
	}


	/**
	 * Sets the geometry editor listener.
	 * 
	 * @param l
	 * 	listener
	 */
	public void setGeometryEventListener(GeometryEditorListener l){
		this.l = l;
	}

	/**
	 * Scene has been changed and views must
	 * be updated.
	 * 
	 * @param sender
	 * 	methis caller
	 */
	public void sceneChanged(Object sender){
		sceneXY.updateView(sender);
		sceneZY.updateView(sender);
		sceneXZ.updateView(sender);
		if(sceneTEX != null){
			sceneTEX.updateView(sender);
		}
		if(this.l != null){
			this.l.sceneChanged(sender);
		}
	}

	/**
	 * Geometry data has been changed and
	 * listeners must be updated.
	 * 
	 * @param sender
	 * 	method caller
	 */
	public void dataChanged(Object sender){
		if(this.l != null){
			this.l.dataChanged(sender);
		}
	}

	/**
	 * Sets the edge visibility property of <code>Scene2D</code> objects.
	 * 
	 * @param visibility
	 * 	true - edges are visible; false - not
	 */
	public void setEdgeVisibility(boolean visibility){
		sceneXY.setEdgeVisibility(visibility);
		sceneZY.setEdgeVisibility(visibility);
		sceneXZ.setEdgeVisibility(visibility);
		if(sceneTEX != null){
			sceneTEX.setEdgeVisibility(visibility);
		}
	}


	/**
	 * Replaces <code>Scene3D</code> object with
	 * <code>Scene2D</code> that shows texture coordinates.
	 */
	public void openTextureEditor(){
		try{
			sceneTEX = new Scene2D(Axes2D.TEX);
			this.remove(9);
			this.add(sceneTEX, 9);
			this.remove(10);
			this.add(new CameraPanel(sceneTEX, 70, 70, 0, 0, ButtonPanelLayout.NEGATIVE), 10);
			GeometryManipulator gm = new GeometryManipulator((J3DeGeometryArray)geom, GeometryManipulator.TEX_COORDS);
			gm.setScaleAxes(this.gm.getScaleAxes());
			this.sceneTEX.setGeometryManipulator(gm);
			this.sceneTEX.setGeometryEditorListener(this);
			if(object.getAppearance().getTexture() != null){
				int imgComps = object.getAppearance().getTexture().numberOfImageComponents();
				if(imgComps > 0){
					this.sceneTEX.setImage(object.getAppearance().getTexture().getImageComponent(0).getImage(0));
				}
			}
			//TODO
			gm.setTexCoordSet(0);
			this.validate();
			this.repaint();
		} catch(Throwable t){
			Utilities.out.throwError(t);
		}
	}

	/**
	 * Replaces <code>Scene2D</code> that shows texture coordinates
	 * with <code>Scene3D</code> object.
	 */
	public void closeTextureEditor(){
		try{
			this.remove(9);
			this.add(this.panel3D.getScene3D(), 9);
			this.remove(10);
			this.add(new CameraPanel(panel3D.getScene(), 70, 70, 0, 0, ButtonPanelLayout.NEGATIVE), 10);
			this.validate();
			this.repaint();
			this.sceneTEX = null;
		} catch(Throwable t){
			Utilities.out.throwError(t);
		}
	}

	/**
	 * Changes the size of geometry.
	 * 
	 * @param newSize
	 * 	new size
	 * 
	 * @see j3deditor.bin.hierarchy.GeometryManipulator#changeGeometrySize(int newSize)
	 */
	public void changeGeometrySize(int newSize){
		try{
			J3DeGeometryArray g = this.gm.changeGeometrySize(newSize);
			if(g != null){
				this.geom = g;
				this.object.setGeometry(g, this.geometryIndex);
				this.panel3D.setGeometry(this.geom);
				this.sceneChanged(this);
				this.dataChanged(this);
			}
		} catch(Throwable t){
			Utilities.out.throwError(t);
		}
	}

	/**
	 * Returns the default <code>GeometryManipulator</code>.
	 * 
	 * @return the default <code>GeometryManipulator</code>
	 */
	public GeometryManipulator getDefaultGeometryManipulator(){
		return this.gm;
	}

	/**
	 * Updates scale axes.
	 * 
	 * @param axes
	 * 	new values of scale axes
	 * 
	 * @see j3deditor.bin.hierarchy.GeometryManipulator#setScaleAxes(int[] axes)
	 */
	public void updateScaleAxes(int[] axes){
		this.gm.setScaleAxes(axes);
		if(this.sceneTEX != null && this.sceneTEX.getGeometryManipulator() != null){
			this.sceneTEX.getGeometryManipulator().setScaleAxes(axes);
		}
	}

	/**
	 * Disposes all content.
	 */
	public void cleanUp(){
		ComponentUtils.removeChildren(this);
		this.object = null;
		this.geom = null;
		this.gm = null;
		this.sceneXY = null;
		this.sceneZY = null;
		this.sceneXZ = null;
		this.sceneTEX = null;
		this.panel3D.cleanUp();
		this.panel3D = null;
		this.l = null;
	}

}
