package j3deditor.bin.hierarchy.util;


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

import j3deditor.bin.util.ByteConverter;
import j3deditor.bin.util.Utilities;

import com.sun.j3d.utils.scenegraph.io.SceneGraphStreamWriter;

import java.io.FileOutputStream;
import java.io.OutputStream;

import java.util.HashMap;

import javax.media.j3d.BranchGroup;


/**
 * Tool for saving scenes.
 * Uses <code>com.sun.j3d.utils.scenegraph.io.SceneGraphStreamWriter</code>
 * to save <code>BranchGroup</code>.
 *
 * @author Risto Seene
 * 
 * @see com.sun.j3d.utils.scenegraph.io.SceneGraphStreamWriter
 */
public class ProjectSaver extends HierarchyWorker{
	
	/** Specifies the version of this saver */
	private static final float version = 1.0f;

	/** scene the <code>BranchGroup</code> will be saved from */
	private J3DeScene scene;

	/** output stream where the <code>SceneGraphStreamWriter</code> writes to */
	private OutputStream out = null;
	
	/** path of the scene file */
	private String filePath;


	/**
	 * Creates an instance of <code>ProjectSaver</code>
	 * which is able to save <code>BranchGroup</code> from the given scene.
	 *
	 * @param scene
	 *	an instance of <code>J3DeScene</code>
	 *	this loader can save from
	 */
	public ProjectSaver(J3DeScene scene){
		this.scene = scene;
	}


	/**
	 * Saves the scene to the given file.
	 *
	 * @param filePath
	 *	path of the file the scene will be saved to
	 */
	public void saveProject(String filePath){
		this.filePath = filePath;
		this.execute();
	}
	
	
	/**
	 * Performs the saving process in a background thread.
	 * 
	 * @return nothing
	 * @throws Exception
	 * 	if an error occurs
	 */
	protected Void doInBackground() throws Exception{
		try{
	                    out = new FileOutputStream(filePath);
	                    writeData();
	                    writeBranchGroup();
	                    out.close();
	          } catch(Throwable t){
			Utilities.out.throwError(t);
	                    try{
	                              out.close();
	                    } catch(Throwable t1){
	                    }
	          }
		
		return null;
	}


	/**
	 * Writes the transformation attributes of the camera
	 *
	 * @exception Exception
	 *	if writing to stream fails
	 */
	private void writeData() throws Exception{
		out.write(ByteConverter.getBytes("<J3DE>"));
		out.write(ByteConverter.getBytes(version));
		scene.getCamera().writeData(out);
		out.write(ByteConverter.getBytes("</J3DE>"));
	}


	/**
	 * Saves the <code>BranchGroup</code> to file
	 *
	 * @see com.sun.j3d.utils.scenegraph.io.SceneGraphStreamWriter#writeBranchGraph(BranchGroup bg, HashMap hm)
	 */
	private void writeBranchGroup(){
		SceneGraphStreamWriter sgsWriter = null;
		try{
			sgsWriter = new SceneGraphStreamWriter(out);

			BranchGroup branchGroup = scene.getBranchGroup();
			sgsWriter.writeBranchGraph(branchGroup, getHashMap());
			sgsWriter.close();
		} catch(Throwable t){
			Utilities.out.throwError(t);
			try{
				sgsWriter.close();
			} catch(Throwable t1){
			}
		}
	}


	/**
	 * Creates a hashmap of the objects and their names
	 *
	 * @return Returns a hashmap of the objects and their names
	 */
	private HashMap getHashMap(){
		HashMap hm = new HashMap();

		for(int i = 0; i < scene.getNumberOfChildren(); i++){
			if(scene.getChild(i) instanceof J3DeObject){
				J3DeObject object = (J3DeObject)(scene.getChild(i));
				hm.put(object.toString(), object.getObject());
			}
		}

		return hm;
	}
	
	
	/**
	 * Notifies all listeners that object(s) have been altered.
	 */
	protected void done(){
		super.done();
		notifyListeners(new HierarchyEvent(this, HierarchyEvent.OBJECT_ALTERATION));
	}

}
