package j3deditor.bin.editor;

import j3deditor.bin.editor.tools.GeometryPanel;

import j3deditor.bin.hierarchy.util.HierarchyEvent;
import j3deditor.bin.hierarchy.util.HierarchyEventListener;

import j3deditor.bin.editor.util.MainLayout;

import j3deditor.bin.hierarchy.GeometryManipulator;
import j3deditor.bin.hierarchy.HierarchyNode;
import j3deditor.bin.hierarchy.J3DeObject;
import j3deditor.bin.hierarchy.J3DeScene;

import j3deditor.bin.hierarchy.J3DeShape3D;
import j3deditor.bin.hierarchy.view.HierarchyViewer;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;


/**
 * Root element of the scene-editor.
 * Contains <code>HierarchyViewer</code> and <code>SceneEditor</code>.
 * Mediates events between editor frame and panels attached to this panel
 *
 * @author Risto Seene
 */
public class EditorPanel extends JPanel implements HierarchyEventListener, TreeSelectionListener{


	/** the scene this editor manipulates */
	private J3DeScene node;

	/** frame this panel is attached to */
	private EditorFrame frame;


	/** hierarchy visualizer */
	private HierarchyViewer hview;

	/** reference to editor panel */
	private JPanel editor;

	/** scene editor */
	private SceneEditor sEditor;

	/** geometry editor */
	private GeometryEditor gEditor;


	/**
	 * Creates an instance of <code>EditorPanel</code>
	 * which is attached to the given frame and
	 * contains the scene-editor for the given scene.
	 *
	 * @param node
	 *	the scene this editor manipulates
	 * @param frame
	 *	frame this panel is attached to
	 */
	public EditorPanel(J3DeScene node, EditorFrame frame){
		this.node = node;
		this.frame = frame;

		hview = new HierarchyViewer(node);
		editor = sEditor = new SceneEditor(node);

		setLayout(new MainLayout());
		add(new JScrollPane(hview));
		add(editor);

		((SceneEditor)editor).setEditorEventListener(this);
		hview.setSelectionListener(this);

		updateHierarchy();
	}


	/**
	 * Updates the hierarchy visualizer an scene-editor attached to this panel.
	 */
	public void updateHierarchy(){
		hview.setSelection();
		((SceneEditor)editor).getScene().updateScene();
	}


	/**
	 * Processes events that have made changes to scene hierarchy.
	 *
	 * @param e
	 *	event sent by any component of this scene-editor
	 */
	public void hierarchyEventOccured(HierarchyEvent e){
		if(e.getEventType() == HierarchyEvent.CAMERA_TRANSFORMATION){
		}
		else if(e.getEventType() == HierarchyEvent.OBJECT_TRANSFORMATION){
			((SceneEditor)editor).updateValues();
		}
		else if(e.getEventType() == HierarchyEvent.OBJECT_ADDITION){
			updateHierarchy();
		}
		else if(e.getEventType() == HierarchyEvent.OBJECT_REMOVAL){
			selectionChanged();
			frame.selectionChanged();
			((SceneEditor)editor).updateTools();
			updateHierarchy();
		}
		else if(e.getEventType() == HierarchyEvent.OBJECT_SELECTION){
			selectionChanged();
			frame.selectionChanged();
			((SceneEditor)editor).updateTools();
		}
		else if(e.getEventType() == HierarchyEvent.OBJECT_ALTERATION){
			updateHierarchy();
		}
	}


	/**
	 * Method to listen events sent by <code>HierarchyViewer</code>.
	 *
	 * @param e
	 *	<code>TreeSelectionEvent</code> sent by <code>HierarchyViewer</code>
	 *	attached to this panel
	 */
	public void valueChanged(TreeSelectionEvent e){
		hierarchyEventOccured(new HierarchyEvent(this, HierarchyEvent.OBJECT_SELECTION));
	}


	/**
	 * Updates scene-editor elements accordingly to
	 * selected hierarchy nodes
	 */
	private void selectionChanged(){
		boolean state = false;
		HierarchyNode[] objects = node.getSelectedObjects();
		if(objects != null){
			for(int i = 0; i < objects.length; i++){
				if(objects[i] instanceof J3DeObject){
					state = true;
					break;
				}
			}
		}
		((SceneEditor)editor).updateTransformPanel(state);
	}


	/**
	 * Opens geometry editor in this editor frame.
	 * 
	 * @return true if the geometry that was opened has texture
	 */
	public boolean openGeometryEditor(){
		if(node.getSelectedObjects() != null && node.getSelectedObjects()[0] instanceof J3DeShape3D){
			gEditor = new GeometryEditor((J3DeShape3D)(node.getSelectedObjects()[0]));
			GeometryManipulator gm = gEditor.getDefaultGeometryManipulator();
			GeometryPanel gPanel = new GeometryPanel(gm);
			gEditor.setGeometryEventListener(gPanel);
			gPanel.setGeometryEventListener(gEditor);
			boolean hasTex = gm.hasTexture();
			this.removeAll();
			editor = gEditor;
			add(gPanel, 0);
			add(editor, 1);
			validate();
			editor.repaint();
			return hasTex;
		}
		return false;
	}

	/**
	 * Opens scene editor.
	 */
	public void openSceneEditor(){
		this.removeAll();
		editor = sEditor;
		add(new JScrollPane(this.hview));
		add(editor, 1);
		validate();
		editor.repaint();
		gEditor.cleanUp();
		gEditor = null;
	}

	/**
	 * Returns the geometry editor.
	 * 
	 * @return the geometry editor
	 */
	protected GeometryEditor getGeometryEditor(){
		return this.gEditor;
	}


	/**
	 * Overrided <code>paintComponent</code> method of <code>JPanel</code>.
	 * Paints the background of this panel.
	 * 
	 * @param graphics
	 * 	graphics
	 */
	public void paintComponent(Graphics graphics){
		Graphics2D g = (Graphics2D)graphics;
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		super.paintComponent(g);

		int x = getWidth();
		int y = getHeight();

		GradientPaint gp1 = new GradientPaint((int)(0.2 * x), (int)(-0.2 * y), new Color(200, 200, 200), (int)(0.8 * x), (int)(1.2 * y), new Color(0, 0, 0));
		g.setPaint(gp1);
		g.fillRect(0, 0, x, y);
	}

	/**
	 * Disposes all content.
	 */
	public void cleanUp(){
	          this.node = null;
		this.frame = null;
		this.hview = null;
		this.editor = null;
		if(sEditor != null){
			sEditor.cleanUp();
		}
		this.sEditor = null;
		if(gEditor != null){
			gEditor.cleanUp();
		}
		this.gEditor = null;
	}

}
