/*
 * IsoVolume.java
 *
 * Copyright (c) 2007  Vreixo Formoso
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * See COPYING file for details.
 */

package org.pykix.libburnia.libisofs;

import java.io.File;
import java.io.FileNotFoundException;

import org.pykix.libburnia.bindings.Proxy;
import org.pykix.libburnia.libburn.BurnSource;

/**
 * ISO-9660 Data volume.
 * 
 * <p>
 * This is the primary object to keep track of files and directory to be
 * recorded in an ISO-9660 image. 
 * 
 * <p>
 * To create an ISO image, you have to create a <code>IsoVolume</code>
 * object and add files and/or directories to it. After doing so, you will
 * want to get a {@link BurnSource} to actually write the image to a file or
 * disc. To do this, you have to create an {@link IsoVolSet} with this object.
 *  
 * @see IsoVolSet
 * 
 * TODO current implementation forces user to keep a reference to this
 * object while still having a reference to an IsoTreeNode from this. This
 * is because of automatic memory management. Underlying iso_volume is 
 * deleted when Java object gets out of scope, and this causes all iso_tree_node
 * to be also deleted, thus <i>invalidating</i> java objects. Without
 * modifying libburnia code, the only way to solve this (I think) is add a ref
 * from each IsoTreeNode. Because of space efficience and normal library usage
 * I think current solution is better. 
 * When added to IsoVolSet there is no problem if this
 * goes out of scope (while keeping ref. to IsoVolSet), because ref count
 * is incremented and underlying object is not deleted.
 * 
 * @author Vreixo Formoso
 * @since 0.1
 */
public class IsoVolume extends Proxy {
	
	/**
	 * Create a new volume.
	 * 
	 * @param volumeId
	 * 		Id for the volume, can be null if you wish to set it later.
	 * @param publisherId
	 * 		Id for the publish, can be null if you wish to set it later.
	 * @param dataPreparerId
	 * 		Id for the data prepared, can be null if you wish to set it later.
	 */
	public IsoVolume(String volumeId, String publisherId, 
			String dataPreparerId) {
		
		super( iso_volume_new(volumeId, publisherId, dataPreparerId) );
	}
	
	/**
	 * Create a new volume.
	 * 
	 * @param volumeId
	 * @param publisherId
	 * @param dataPreparerId
	 * @param root
	 */
	public IsoVolume(String volumeId, String publisherId, 
			String dataPreparerId, IsoTreeNode root) {
		
		super( iso_volume_new_with_root(volumeId, publisherId, dataPreparerId, 
				pointerOf(root)) );
	}
	
	/**
	 * Get the root directory.
	 * @return
	 */
	public IsoTreeNode getRoot() {
		
		long ptr = iso_volume_get_root( pointerOf(this) );

		if (ptr == 0) {
			return null;
		}
		
		IsoTreeNode node = (IsoTreeNode) proxyFor(ptr);
		
		if (node == null) {
			node = new IsoTreeNode(ptr);
		}
		/*
		 * FIXME a problem here is that if this IsoVolume gets out of
		 * scope, C iso_volume will be freed, thus deleting the 
		 * iso_tree_node, and ptr is pointing to an invalid C object, which
		 * can lead to problems. This can also happen in C code if an user
		 * explicity free the iso_volume, but in Java is really a problem
		 * because the automatic memory management 
		 */
		return node;
	}

	/**
	 * Fill in the volume identifier.
	 * @param volumeId
	 */
	public void setVolumeId(String volumeId) {
		iso_volume_set_volume_id(pointerOf(this), volumeId);
	}

	/**
	 * Fill in the publisher.
	 * @param publisherId
	 */
	public void setPublisherId(String publisherId) {
		iso_volume_set_publisher_id(pointerOf(this), publisherId);
	}

	/**
	 * Fill in the data preparer.
	 * @param dataPreparerId
	 */
	public void setDataPreparerId(String dataPreparerId) {
		iso_volume_set_data_preparer_id(pointerOf(this), dataPreparerId);
	}
	
	/**
	 * Locate a node by its path on disc.
	 * 
	 * @param path
	 * 		The path, in the image, of the file.
	 * @return
	 * 		The node found or <code>null</code>.
	 */
	/*
	 * Take care that this is really a wrapp for iso_tree_volume_path_to_node
	 */
	public IsoTreeNode getNode(String path) {
		
		long ptr = iso_tree_volume_path_to_node(pointerOf(this), path);

		if (ptr == 0) {
			return null;
		}

		//FIXME same problems as in getRoot
		return (ptr == 0) ? null : new IsoTreeNode(ptr);
	}

	/**
	 * Add a file or a directory (recursively) to the volume by specifying 
	 * its path on the volume.
	 * 
	 * @param discPath
	 * 		The path on the disc at which to add the file/directory.
	 * @param path
	 * 		The path, on the local filesystem, of the file.
	 * @return
	 * 		The node for the file or <code>null</code> if the parent doesn't 
	 * 		exists on the disc.
	 */
	public IsoTreeNode addPath(String discPath, String path) {
		
		long ptr = iso_tree_volume_add_path(pointerOf(this), discPath, path);

		if (ptr == 0) {
			return null;
		}

		IsoTreeNode node = (IsoTreeNode) proxyFor(ptr);
		
		if (node == null) {
			node = new IsoTreeNode(ptr);
		}
		//FIXME same problems as in getRoot
		return node;
	}
	
	/**
	 * Like {@link #addPath(String, String) addPath}, but local path is
	 * passed as a {@link File}
	 * 
	 * @param discPath
	 * 		The path on the disc at which to add the file/directory.
	 * @param path
	 * 		The path, on the local filesystem, of the file.
	 * @return
	 * 		The node for the file or <code>null</code> if the parent doesn't 
	 * 		exists on the disc.
	 * @throws FileNotFoundException
	 * 		If passed local path does not exist
	 */
	public IsoTreeNode addPath(String discPath, File path) 
	throws FileNotFoundException {

		if ( !path.exists() ) {
			throw new FileNotFoundException(path.getPath());
		}

		long ptr = iso_tree_volume_add_path(pointerOf(this), discPath, 
				path.getPath());

		if (ptr == 0) {
			return null;
		}

		IsoTreeNode node = (IsoTreeNode) proxyFor(ptr);
		
		if (node == null) {
			node = new IsoTreeNode(ptr);
		}
		//FIXME same problems as in getRoot
		return node;
	}
	
	/**
	 * Creates a new, empty directory on the volume.
	 * 
	 * @param discPath
	 * 		The path on the volume at which to add the directory.
	 * @return
	 * 		The newly created directory or <code>null</code>.
	 */
	public IsoTreeNode addNewDir(String discPath) {
		
		long ptr = iso_tree_volume_add_new_dir(pointerOf(this), discPath);

		if (ptr == 0) {
			return null;
		}
		
		IsoTreeNode node = (IsoTreeNode) proxyFor(ptr);
		
		if (node == null) {
			node = new IsoTreeNode(ptr);
		}
		//FIXME same problems as in getRoot
		return node;
	}	
	
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		
		/* free underlying C object */
		iso_volume_free( pointerOf(this) );
	}
	
	private static native long iso_volume_new(String volumeId, 
			String publisherId,	String dataPreparerId);

	private static native long iso_volume_new_with_root(String volumeId,
			String publisherId, String dataPreparerId, long root);
	
	private static native void iso_volume_free(long volume);
	
	private static native long iso_volume_get_root(long volume);
	
	private static native void iso_volume_set_volume_id(long volume,
			String volumeId);

	private static native void iso_volume_set_publisher_id(long volume,
			String publisherId);

	private static native void iso_volume_set_data_preparer_id(long volume,
			String data_preparer_id);
	
	private static native long iso_tree_volume_path_to_node(long volume, 
			String path);
	
	private static native long iso_tree_volume_add_path(long volume,
			String discPath, String path);

	private static native long iso_tree_volume_add_new_dir(long volume,
			String discPath);
	
	static {
		System.loadLibrary("java-libburn-0.1");
	}
	
}
