/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */

/**
 * Create an ISO-9660 data volume with Rock Ridge and Joliet extensions.
 * Usage is easy:
 *  - Create a new volume.
 *  - Add files and directories.
 *  - Write the volume to a file or create a burn source for use with Libburn.
 */

#ifndef LIBISO_LIBISOFS_H
#define LIBISO_LIBISOFS_H

#include <sys/types.h>
#include <stdint.h>

/* #include <libburn.h> */
struct burn_source;

/**
 * Data volume.
 * @see volume.h for details.
 */
struct iso_volume;

/**
 * A set of data volumes.
 * @see volume.h for details.
 */
struct iso_volset;

/**
 * A node in the filesystem tree.
 * 
 * This is opaque struct that represent any kind of nodes. When needed,
 * you can get the type with iso_tree_node_get_type and cast it to the
 * appropiate subtype:
 * 
 * iso_tree_node_dir
 * iso_tree_node_file
 * iso_tree_node_symlink
 * 
 * \see tree.h
 */
struct iso_tree_node;

/**
 * The type of an iso_tree_node.
 * When an user gets an iso_tree_node from libisofs, (s)he can use
 * iso_tree_node_get_type to get the current type of the node, and then
 * cast to the appropriate subtype. For example:
 * 
 * ...
 * struct iso_tree_node *node = iso_tree_iter_next(iter);
 * if ( iso_tree_node_get_type(node) == LIBISO_NODE_DIR ) {
 * 		struct iso_tree_node_dir *dir = (struct iso_tree_node_dir *)node;
 *      ...
 * }
 * 
 * Useful macros are provided.
 */
enum iso_tree_node_type {
	LIBISO_NODE_DIR,
	LIBISO_NODE_FILE,
	LIBISO_NODE_SYMLINK,
	LIBISO_NODE_BOOT
};

#define LIBISO_ISDIR(n) (iso_tree_node_get_type(n) == LIBISO_NODE_DIR)
#define LIBISO_ISREG(n) (iso_tree_node_get_type(n) == LIBISO_NODE_FILE)
#define LIBISO_ISLNK(n) (iso_tree_node_get_type(n) == LIBISO_NODE_SYMLINK)

/**
 * A directory in the filesystem tree.
 * The first member of this is an iso_tree_node.
 * \see tree.h
 */
struct iso_tree_node_dir;

/**
 * A node in the filesystem tree that represents a regular file
 */
struct iso_tree_node_file;

/**
 * A node in the filesystem tree that represents a symbolic link
 */
struct iso_tree_node_symlink;

/**
 * A node that represents an El-Torito file.
 */
struct iso_tree_node_boot;

/**
 * Information about El-Torito boot image.
 * \see eltorito.h
 */
struct el_torito_boot_image;

/** Iterator for dir children. */
struct iso_tree_iter;

/**
 * The procedence of the node.
 */
enum tree_node_from {
	/** The node has been added by the user */
	LIBISO_NEW = 0,
	/** 
	 * The node comes from a previous image. That can be from a previous
	 * session on disc, or from an ISO file we want to modify.
	 */
	LIBISO_PREVIMG
};

/**
 * Extensions addition to ECMA-119 (ISO-9660) image. Usage of at least
 * one of these flags is highly recommended if the disc will be used on a
 * modern OS.  
 */
enum ecma119_extension_flag {
	/** 
	 * Add the standard Rock Ridge extensions. This adds POSIX filesystem 
	 * features to the ECMA-119 image. Thus, usage of this flag is highly
	 * recommended for images used on GNU/Linux systems. With the usage
	 * of RR extension, the resulting image will have long filenames (up to
	 * 255 characters), deeper directory structure, POSIX permissions and
	 * owner info on files and directories, support for symbolic links or 
	 * special files... All that attributes can be modified/setted with the
	 * appropiate function. 
	 */
	ECMA119_ROCKRIDGE	= (1<<0),
	/**
	 * Add the non-standard Joliet extension to the image. This extension is
	 * heavily used in Microsoft Windows systems, so if you plan to use your
	 * disc on such a system you should add this extension. Usage of Joliet
	 * supplies longer filesystem length (up to 64 unicode characters), and
	 * deeper directory structure.
	 */
	ECMA119_JOLIET		= (1<<1)
};

/**
 * Flag used to hide a file in the RR/ISO or Joliet tree.
 * 
 * \see iso_tree_node_set_hidden
 */
enum hide_node_flag {
	LIBISO_HIDE_ON_RR = 1 << 0,
	LIBISO_HIDE_ON_JOLIET = 1 << 1
};

/**
 * El-Torito bootable image type.
 */
enum eltorito_boot_media_type {
	ELTORITO_FLOPPY_EMUL,
	ELTORITO_HARD_DISC_EMUL,
	ELTORITO_NO_EMUL
};

/**
 * ISO-9660 (ECMA-119) has important restrictions in both file/dir names
 * and deep of the directory hierarchy. These are intented for compatibility
 * with old systems, and most modern operative system can safety deal with
 * ISO filesystems with relaxed constraints. 
 * You can use some of these flags to generate that kind of filesystems with
 * libisofs. Of course, all these options will lead to an image not conforming
 * with ISO-9660 specification, so use them with caution. 
 * Moreover, note that there are much better options to have an ISO-9660 image
 * compliant with modern systems, such as the Rock Ridge and Joliet extensions,
 * that add support for longer filenames, deeper directory hierarchy and even
 * file permissions (in case of RR), while keeping a standard ISO structure
 * suitable for old systems. 
 * Thus, in most cases you don't want to use the relaxed constraints.
 */
enum ecma119_relaxed_constraints_flag {
	ECMA119_OMIT_VERSION_NUMBERS = (1<<0),
		/**<
		 * ISO-9660 requires a version number at the end of each file name.
		 * That number is just ignored on most systems, so you can omit them
		 * if you want.
		 */
	ECMA119_37_CHAR_FILENAMES = (1<<1) | (1<<0), 
		/**<
		 * Allow ISO-9660 filenames to be up to 37 characters long. The extra
		 * space is taken from the version number, so this option involves
		 * no version number
		 */
	ECMA119_NO_DIR_REALOCATION = (1<<2),
		/**<
		 * In ISO-9660 images the depth of the directory hierarchy can't be
		 * greater than 8 levels. In addition, a path to a file on disc can't
		 * be more than 255 characteres. Use the ECMA119_NO_DIR_REALOCATION
		 * to disable this restriction.
		 */
	ECMA119_RELAXED_FILENAMES = (1<<3)
		/**<
		 * Allow filenames with any character. Note that with this flag, the
		 * filename provide by the user will be used without any modification
		 * other that a truncate to max. length.
		 */
};

/**
 * Holds the options for the image generation.
 */
struct ecma119_source_opts {
	int volnum; /**< The volume in the set which you want to write (usually 0) */
	int level;  /**< ISO level to write at. */
	int flags;  /**< Which extensions to support. */
	int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */
	
	unsigned int copy_eltorito:1;
		/**<
		 * In multisession discs, select whether to copy el-torito catalog
		 * and boot image. Copy is needed for isolinux images, that need to
		 * be patched. However, it can lead to problems when the image is 
		 * not present in the iso filesystem, because we can't figure out
		 * its size. In those cases, we only copy 1 block of data.
		 */
	
	unsigned int no_cache_inodes:1; 
	            /**< If use inode caching or not. Set it to 1 to prevent
	             *   inode caching.
	             *   Usage of inode caching allows detection of hard-links,
	             *   which contents are only written once to disc this way.
	             *   Don't use inode caching in systems with non unique inodes
	             *   per device.
	             */  
	unsigned int sort_files:1;
	            /**< If files should be sorted based on their weight. */
	unsigned int default_mode:1;
	            /**<
	             * The default values for files and directory permissions,
	             * gid and uid. This option can be overwritten when set
	             * one of the following.
	             * 0 to use useful values, 1 to use node modes (this are
	             * the same as filesystem ones if not changed after added
	             * to tree). 
	             */
	unsigned int replace_dir_mode:1;
	            /**<
	             * When 1, permissions for all dirs will be replaced by the
	             * specified in dir_mode field.
	             */
	unsigned int replace_file_mode:1;
	            /**<
	             * When 1, permissions for all files will be replaced by the
	             * specified in file_mode field.
	             */
	unsigned int replace_uid:1;
	            /**<
	             * When 1, uid of all nodes (both files and dirs) will be
	             * replaced by the specified in uid field.
	             */
	unsigned int replace_gid:1;
	            /**<
	             * When 1, gid of all nodes (both files and dirs) will be
	             * replaced by the specified in gid field.
	             */
	mode_t dir_mode; /**< Mode to use on dirs when replace_dir_mode is set. */
	mode_t file_mode; /**< Mode to use on files when replace_file_mode is set. */
	gid_t gid; /**< gid to use when replace_gid is set. */
	uid_t uid; /**< uid to use when replace_uid is set. */
	char *input_charset; /**< NULL to use default charset */
	char *ouput_charset; /**< NULL to use default charset */
	uint32_t ms_block;
				/**< 
				 * Start block for multisession. When this is greater than 0, 
				 * it's suppossed to be the lba of the next writable address 
				 * on disc; all block lba on image will take this into account,
				 * and files from a previous session will not be written on 
				 * image. This behavior is only suitable for images to be
				 * appended to a multisession disc.
				 * When this is 0, no multisession image will be created. If 
				 * some files are taken from a previous image, its contents
				 * will be written again to the new image. Use this with new
				 * images or if you plan to modify an existin image. 
				 */
	struct data_source* src;
				/**<
				 * When modifying a image, this is the source of the original
				 * image, used to read file contents.
				 * Otherwise it can be NULL.
				 */
	uint8_t *overwrite;
				/**<
				 * When not NULL, it should point to a buffer of at least 
				 * 64KiB, where libisofs will write the contents that should 
				 * be written at the beginning of a overwriteable media, to
				 * grow the image.
				 * You shoudl initialize the buffer either with 0s, or with
				 * the contents of the first blocks of the image you're
				 * growing. In most cases, 0 is good enought.
				 */
};

/**
 * Options for image reading.
 * There are four kind of options:
 * - Related to multisession support.
 *   In most cases, an image begins at LBA 0 of the data source. However,
 *   in multisession discs, the later image begins in the last session on
 *   disc. The block option can be used to specify the start of that last
 *   session.
 * - Related to the tree that will be read.
 * 	 As default, when Rock Ridge extensions are present in the image, that
 *   will be used to get the tree. If RR extensions are not present, libisofs
 *   will use the Joliet extensions if available. Finally, the plain ISO-9660
 *   tree is used if neither RR nor Joliet extensions are available. With
 *   norock, nojoliet, and preferjoliet options, you can change this
 *   default behavior.
 * - Related to default POSIX attributes.
 *   When Rock Ridege extensions are not used, libisofs can't figure out what
 *   are the the permissions, uid or gid for the files. You should supply
 *   default values for that.
 * - Return information for image.
 *   Both size, hasRR and hasJoliet will be filled by libisofs with suitable values.
 *   Also, error is set to non-0 if some error happens (error codes are
 *   private now)
 */
struct ecma119_read_opts {
	uint32_t block; /** Block where the image begins, usually 0, can be
	                 *  different on a multisession disc.
	                 */
	
	unsigned int norock:1; /*< Do not read Rock Ridge extensions */
	unsigned int nojoliet:1; /*< Do not read Joliet extensions */
	unsigned int preferjoliet:1; 
			/*< When both Joliet and RR extensions are present, the RR
			 *  tree is used. If you prefer using Joliet, set this to 1. */
			 
	uid_t uid; /**< Default uid when no RR */
	gid_t gid; /**< Default uid when no RR */
	mode_t mode; /**< Default mode when no RR (only permissions) */
	//TODO differ file and dir mode
	//option to convert names to lower case?
	
	/* modified by the function */
	unsigned int hasRR:1; /*< It will be set to 1 if RR extensions are present,
	                          to 0 if not. */
	unsigned int hasJoliet:1; /*< It will be set to 1 if Joliet extensions are 
	                              present, to 0 if not. */
	uint32_t size; /**< Will be filled with the size (in 2048 byte block) of 
	                *   the image, as reported in the PVM. */
	int error;
};

/**
 * Data source used by libisofs for reading an existing image.
 * It contains suitable methods to read  arbitrary block. Usually, the block
 * size is 2048 bytes.
 */
struct data_source {
	
	/** 
	 * Reference count for the data source. Should be 1 when a new source
	 * is created.  Increment it to take a reference for yourself. Use
	 * data_source_free to destroy your reference to it. 
	 */
	int refcount;

	/** 
	 * Read data from the source.
	 * @param lba Block to be read.
	 * @param buffer Buffer where the data will be written. Its size must
	 *               be at least 2048 bytes.
	 * @return
	 * 		0 if ok, < 0 on error
	 */
	int (*read_block)(struct data_source *src, int lba, unsigned char *buffer);

	/** Get the size (number of block) of the source's data */
	int (*get_size)(struct data_source *);

	/** Clean up the source specific data */
	void (*free_data)(struct data_source *);

	/** Source specific data */
	void *data;
};

/**
 * This will hold the error code for some functions, if them fail.
 */
int libisofs_errno;

/* an unexpected internal error */
#define INTERNAL_ERROR -1
/* file don't exists, or can't be stat'ed */
#define NO_FILE 1
/* user haven't read access to file */
#define NO_READ_ACCESS 2
/* unexpected file type, eg., passing a dir instead of a regular file */
#define UNEXPECTED_FILE_TYPE 3
/* invalid boot image size */
#define ELTORITO_WRONG_IMAGE_SIZE 4
/* invalid image */
#define ELTORITO_WRONG_IMAGE 5

/**
 * Controls the bahavior of iso_tree_radd_dir function
 */
struct iso_tree_radd_dir_behavior {
	char** excludes; /**< List of paths (file or directory) to be ignored. */
	//int follow_sym_link;
	int stop_on_error; /**< Stop when an error was found?. */
	int error; /**< set to 1 on error */
	//int notify_errors;
	//char** errors;
};

/**
 * Initialize libisofs. You must call this before any usage of the library.
 * @return 1 on success, 0 on error 
 */
int iso_init();

/**
 * Finalize libisofs.
 */
void iso_finish();

/**
 * Create a new volume.
 * The parameters can be set to NULL if you wish to set them later.
 */
struct iso_volume *iso_volume_new(const char *volume_id,
				  const char *publisher_id,
				  const char *data_preparer_id);

struct iso_volume *iso_volume_new_with_root(const char *volume_id,
					    const char *publisher_id,
					    const char *data_preparer_id,
					    struct iso_tree_node_dir *root);

/**
 * Free a volume.
 */
void iso_volume_free(struct iso_volume *volume);

void iso_volset_ref(struct iso_volset *volset);

/**
 * Free a set of data volumes.
 */
void iso_volset_free(struct iso_volset *volume);

/**
 * Get a volume from a volume set.
 */
struct iso_volume *iso_volset_get_volume(struct iso_volset *volset, int volnum);

/**
 * Get the root directory for a volume.
 */
struct iso_tree_node_dir *iso_volume_get_root(const struct iso_volume *volume);

/**
 * Fill in the volume identifier for a volume.
 */
void iso_volume_set_volume_id(struct iso_volume *volume,
			      const char *volume_id);

/** 
 * Get the volume identifier. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_volume_id(struct iso_volume *volume);

/**
 * Fill in the publisher for a volume.
 */
void iso_volume_set_publisher_id(struct iso_volume *volume,
				 const char *publisher_id);

/** 
 * Get the publisher of a volume. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_publisher_id(struct iso_volume *volume);

/**
 * Fill in the data preparer for a volume.
 */
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
				     const char *data_preparer_id);

/** 
 * Get the data preparer of a volume. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_data_preparer_id(struct iso_volume *volume);

/**
 * Fill in the system id for a volume. Up to 32 characters.
 */
void iso_volume_set_system_id(struct iso_volume *volume,
				     const char *system_id);

/** 
 * Get the system id of a volume. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_system_id(struct iso_volume *volume);

/**
 * Fill in the application id for a volume. Up to 128 chars.
 */
void iso_volume_set_application_id(struct iso_volume *volume,
				     const char *application_id);

/** 
 * Get the application id of a volume. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_application_id(struct iso_volume *volume);

/**
 * Fill copyright information for the volume. Usually this refers
 * to a file on disc. Up to 37 characters.
 */
void iso_volume_set_copyright_file_id(struct iso_volume *volume,
				     const char *copyright_file_id);

/** 
 * Get the copyright information of a volume. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_copyright_file_id(struct iso_volume *volume);

/**
 * Fill abstract information for the volume. Usually this refers
 * to a file on disc. Up to 37 characters.
 */
void iso_volume_set_abstract_file_id(struct iso_volume *volume,
				     const char *abstract_file_id);

/** 
 * Get the abstract information of a volume. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_abstract_file_id(struct iso_volume *volume);

/**
 * Fill biblio information for the volume. Usually this refers
 * to a file on disc. Up to 37 characters.
 */
void iso_volume_set_biblio_file_id(struct iso_volume *volume,
				     const char *biblio_file_id);

/** 
 * Get the biblio information of a volume. 
 * The returned string is owned by libisofs and should not be freed nor
 * changed.
 */
const char *iso_volume_get_biblio_file_id(struct iso_volume *volume);

/**
 * Create a bootable volume by adding a El-Torito boot image.
 * 
 * This also add a catalog tree node to the image filesystem tree. The tree
 * node for the image will be replaced with a iso_tree_node_boot node, that
 * acts as a placeholder for the real image.
 * 
 * \param volume The volume to make bootable.
 * \param image The tree node with the file to use as default boot image.
 * \param type The boot media type. This can be one of 3 types:
 * 			   - Floppy emulation: Boot image files must be exactly
 *               1200 kB, 1440 kB or 2880 kB.
 *             - Hard disc emulation: The image must begin with a master
 *               boot record with a single image.
 *             - No emulation. You should specify load segment and load size
 *               of image.
 * \param dir The directory node where the boot catalog will be located
 *            in image. Usually both boot catalog and boot image will be
 *            located in the same dir, maybe /boot.
 * \param name The name of the boot catalog.
 * 
 * \return The default El-Torito bootable image. If specified image file
 *         seems to be not correct, this returns NULL and libisofs_errno
 *         is set propertly.
 * 
 * \pre \p volume is a volume without any boot catalog yet
 * \pre \p image is a file tree node already inserted in the volume tree.
 * \pre \p image is a file tree node that refers to a newly added file, not
 *         one from a previous session. FIXME allow prev session files too
 * \pre \p dir is a directory node already inserted in the volume tree.
 * \pre \p name There isn't any dir child with the same name.
 * 
 */
struct el_torito_boot_image*
iso_volume_set_boot_image(struct iso_volume *volume, 
                          struct iso_tree_node *image,
                          enum eltorito_boot_media_type type,
                          struct iso_tree_node_dir *dir, 
                          char *name);

struct el_torito_boot_image* 
iso_volume_set_boot_image_hidden(struct iso_volume *volume, 
                                 const char* path,
                                 enum eltorito_boot_media_type type);

/**
 * Get El-Torito boot image of the volume, if any.
 * 
 * This can be useful, for example, to check if a volume read from a previous
 * session or an existing image is bootable. It can also be useful to get
 * the image and catalog tree nodes. An application would want those, for 
 * example, to prevent the user removing it.
 * 
 * Both tree nodes are owned by libisofs and should not be freed. You can check
 * if the node is already on the tree by getting its parent (note that when
 * reading El-Torito info from a previous image, the nodes might not be on
 * the tree even if you haven't removed them). Remember that you'll need to
 * get a new ref (with iso_tree_node_ref()) before inserting them again to the
 * tree, and probably you will also need to set the name or permissions.
 * 
 * \param imgnode When not NULL, it will be filled with the image tree node, if
 *                any.
 * \param catnode When not NULL, it will be filled with the catalog tree node, 
 *                if any.
 * 
 * \return The default El-Torito bootable image, or NULL is the volume is not
 *         bootable.
 */
struct el_torito_boot_image*
iso_volume_get_boot_image(struct iso_volume *volume,
                            struct iso_tree_node **imgnode,
                            struct iso_tree_node **catnode);

/**
 * Removes the El-Torito bootable image. Both the catalog and image tree nodes 
 * are also removed from the image filesystem tree, if there.
 * If the volume is not bootable (don't have el-torito image) this function is
 * a nop.
 */
void
iso_volume_remove_boot_image(struct iso_volume *volume);

/**
 * Sets the load segment for the initial boot image. This is only for
 * no emulation boot images, and is a NOP for other image types.
 */
void
el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment);

/**
 * Sets the number of sectors (512b) to be load at load segment during
 * the initial boot procedure. This is only for
 * no emulation boot images, and is a NOP for other image types.
 */
void
el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors);

/**
 * Marks the specified boot image as not bootable
 */
void
el_torito_set_no_bootable(struct el_torito_boot_image *bootimg);

/**
 * Specifies that this image needs to be patched. This involves the writting
 * of a 56 bytes boot information table at offset 8 of the boot image file.
 * The original boot image file won't be modified.
 * This is needed for isolinux boot images.
 */
void
el_torito_patch_isolinux_image(struct el_torito_boot_image *bootimg);

/**
 * Locate a node by its path on disc.
 * 
 * \param volume The volume to search in.
 * \param path The path, in the image, of the file.
 *
 * \return The node found or NULL.
 */
struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path);

/**
 * TODO I don't like this kind of functions here. I think it should be
 * in genisofs
 * Add a file or a directory (recursively) to a volume by specifying its path on the volume.
 *
 * \param volume The volume to add the file to.
 * \param disc_path The path on the disc at which to add the disc.
 * \param path The path, on the local filesystem, of the file.
 *
 * \return The node for the file or NULL if the parent doesn't exists on the disc.
 */
//struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume,
//				 					const char *disc_path,
//									const char *path);

/**
 * TODO I don't like this kind of functions here. I think it should be
 * in genisofs
 * Creates a new, empty directory on the volume.
 *
 * \param volume The volume to add the directory to.
 * \param disc_path The path on the volume at which to add the directory.
 *
 * \return A pointer to the newly created directory.
 */
//struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume,
//				 					const char *disc_path);

/**
 * Create a new Volume Set consisting of only one volume.
 * @param volume The first and only volume for the volset to contain.
 * @param volset_id The Volume Set ID.
 * @return A new iso_volset.
 */
struct iso_volset *iso_volset_new(struct iso_volume *volume,
                                  const char *volset_id);


/**
 * Creates a new root dir for a filesystem tree
 */
struct iso_tree_node_dir *iso_tree_new_root();

/**
 * Add a file to a directory.
 *
 * \param path The path, on the local filesystem, of the file.
 * 
 * \pre \p parent is non-NULL.
 * \pre \p path is non-NULL.
 * \return An iso_tree_node_file whose path is \p path and whose parent is 
 *         \p parent. 
 *         On error, returns NULL and libisofs_errno is set appropriately:
 *          NO_FILE if path doesn't point to a valid file.
 *          NO_READ_ACCESS if user haven't read access on file
 *          UNEXPECTED_FILE_TYPE if path doesn't point to a regular file
 */
struct iso_tree_node *iso_tree_add_file(struct iso_tree_node_dir *parent, 
					const char *path);

/**
 * Add a symbolic link to a directory.
 * 
 * \param name The name of the symbolic link
 * \param dest The distination of the link, i.e., the file this link points
 *             to
 * 
 * \pre \p parent, name and dest are non-NULL.
 * 
 * \return An iso_tree_node_symlink
 */
struct iso_tree_node *iso_tree_add_symlink(struct iso_tree_node_dir *parent, 
					const char *name, const char *dest);

/**
 * Add a new, empty directory to the tree.
 *
 * \pre \p parent is non-NULL.
 * \pre \p name is unique among the children and files belonging to \p parent.
 *	Also, it doesn't contain '/' characters.
 *
 * \post \p parent contains a child directory whose name is \p name and whose
 *	POSIX attributes are the same as \p parent's.
 * \return a pointer to the newly created directory.
 */
struct iso_tree_node_dir *iso_tree_add_dir(struct iso_tree_node_dir *parent, 
					const char *name);

/* TODO iso_tree_new_special */

/**
 * Add a file to a directory.
 *
 * \param path The path, on the local filesystem, of the file.
 *
 * \pre \p parent is non-NULL.
 * \pre \p path is non-NULL and is a valid path to a file or directory on the local
 *	filesystem.
 * \return An iso_tree_node whose path is \p path and whose parent is \p parent.
 *         On error, returns NULL and libisofs_errno is set appropriately:
 *          NO_FILE if path doesn't point to a valid file.
 *          NO_READ_ACCESS if user haven't read access on file
 *          UNEXPECTED_FILE_TYPE if path refers to non  supported file type 
 *          (at the momment, only dirs, symlinks and regular
 *          files are supported).
 */
struct iso_tree_node *iso_tree_add_node(struct iso_tree_node_dir *parent,
					const char *path);

/**
 * TODO I don't like this kind of functions here. I think it should be
 * in genisofs
 * 
 * Recursively add an existing directory to the tree.
 * Warning: when using this, you'll lose pointers to files or subdirectories.
 * If you want to have pointers to all files and directories,
 * use iso_tree_add_file, iso_tree_add_node and iso_tree_add_dir.
 *
 * \param path The path, on the local filesystem, of the directory to add.
 *
 * \pre \p parent is non-NULL.
 * \pre \p path is non-NULL and is a valid path to a directory on the local
 *	filesystem.
 */
void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, 
                       struct iso_tree_radd_dir_behavior *behavior);

/**
 * Get the type of an iso_tree_node
 */
enum iso_tree_node_type iso_tree_node_get_type(struct iso_tree_node *node);

/**
 * Set the name of a tree node (using the current locale).
 */
void iso_tree_node_set_name(struct iso_tree_node *node, const char *name);

/**
 * Get the name of a tree node (using the current locale).
 * The returned string belongs to the node and should not be modified nor
 * freed. Use strdup if you really need your own copy.
 */
const char *iso_tree_node_get_name(struct iso_tree_node *node);

/**
 * Set if the node will be hidden in RR/ISO tree, Joliet tree or both.
 * 
 * If the file is setted as hidden in one tree, it won't be included there, so
 * it won't be visible in a OS accessing CD using that tree. For example,
 * GNU/Linux systems access to Rock Ridge / ISO9960 tree in order to see
 * what is recorded on CD, while MS Windows make use of the Joliet tree. If a
 * file is hidden only in Joliet, it won't be visible in Windows systems,
 * while still visible in Linux.
 * 
 * If a file is hidden in both trees, it won't be written to image.
 * 
 * \param node The node that is to be hidden.
 * \param hide_attrs hide_node_flag's to set the trees in which file
 *        will be hidden.
 */
void iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs);

/**
 * Check if a node will be hidden in RR/ISO tree, Joliet tree or both.
 * 
 * @return
 * 		0 if the node won't be hidden, otherwise you can AND the return value
 *      with hide_node_flag's to get in what trees the node will be hidden.
 */
int iso_tree_node_is_hidden(struct iso_tree_node *node);

/**
 * Set the group id for the node. This attribute is only useful when 
 * Rock Ridge extensions are enabled.
 */
void iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid);

/**
 * Get the group id of the node.
 */
gid_t iso_tree_node_get_gid(struct iso_tree_node *node);

/**
 * Set the user id for the node. This attribute is only useful when 
 * Rock Ridge extensions are enabled.
 */
void iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid);

/**
 * Get the user id of the node.
 */
uid_t iso_tree_node_get_uid(struct iso_tree_node *node);

/**
 * Set the permissions for the node. This attribute is only useful when 
 * Rock Ridge extensions are enabled.
 * 
 * \param mode bitmask with the permissions of the node, as specified
 *             in 'man 2 stat'. The file type bitfields will be ignored,
 *             only file permissions will be modified.
 */
void iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode);

/** Get the permissions for the node */
mode_t iso_tree_node_get_permissions(struct iso_tree_node *node);

/** Get the size of the node, in bytes */
off_t iso_tree_node_get_size(struct iso_tree_node *node);

/** Set the time of last modification of the file */
void iso_tree_node_set_mtime(struct iso_tree_node *node, time_t time);

/** Get the time of last modification of the file */
time_t iso_tree_node_get_mtime(struct iso_tree_node *node);

/** Set the time of last access to the file */
void iso_tree_node_set_atime(struct iso_tree_node *node, time_t time);

/** Get the time of last access to the file */
time_t iso_tree_node_get_atime(struct iso_tree_node *node);

/** Set the time of last status change of the file */
void iso_tree_node_set_ctime(struct iso_tree_node *node, time_t time);

/** Get the time of last status change of the file */
time_t iso_tree_node_get_ctime(struct iso_tree_node *node);

/**
 * Sets the order in which a node will be written on image. High weihted files
 * will be written first, so in a disc them will be written near the center.
 * 
 * \param node The node which weight will be changed. If it's a dir, this 
 *             function will change the weight of all its children. For nodes
 *             other that dirs or regular files, this function has no effect.
 * \param w The weight as a integer number, the greater this value is, the 
 *          closer from the begining of image the file will be written.
 */
void iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w);

/**
 * Sets the destination of a symbolic link
 */
void iso_tree_node_symlink_set_dest(struct iso_tree_node_symlink *node, const char *dest);

/**
 * Get the destination of a symbolic link.
 * The returned string is owned by libisofs and should not be freed nor modified.
 */
const char *iso_tree_node_symlink_get_dest(struct iso_tree_node_symlink *node);

/**
 * Get an iterator for the children of the given dir.
 * You can iterate over the children with iso_tree_iter_next. When finished,
 * you should free the iterator with iso_tree_iter_free.
 * You musn't delete a child of the same dir, using iso_tree_node_take() or
 * iso_tree_node_remove(), while you're using the iterator. You can use 
 * iso_tree_node_take_iter() or iso_tree_node_remove_iter() instead.
 * 
 * The usage of an iterator is:
 * 
 * struct iso_tree_iter *iter;
 * struct iso_tree_node *node;
 * iter = iso_tree_node_children(dir);
 * while ( (node = iso_tree_iter_next(iter)) != NULL ) {
 *     // do something with the child
 * }
 * iso_tree_iter_free(iter);
 * 
 * An iterator is intended to be used in a single iteration over the
 * children of a dir. Thus, it should be treated as a temporary object,
 * and free as soon as possible.  
 */
struct iso_tree_iter *iso_tree_node_children(struct iso_tree_node_dir *dir);

/**
 * Get the next child.
 * Take care that the node is owned by libisofs, and will be freed whit the
 * tree it belongs. If you want your own ref to it, call iso_tree_node_ref()
 * on it.
 * This returns NULL if no more children are available.
 */
struct iso_tree_node *iso_tree_iter_next(struct iso_tree_iter *iter);

/**
 * Check if there're more children.
 * @return
 * 		1 if next call to iso_tree_iter_next() will return != NULL,
 * 		0 otherwise
 */
int iso_tree_iter_has_next(struct iso_tree_iter *iter);

/** Free an iteration */
void iso_tree_iter_free(struct iso_tree_iter *iter);

/**
 * Removes a child from a directory.
 * The child is not freed, so you will become the owner of the node. Later
 * you can add the node to another dir (calling iso_tree_add_child), or free
 * it if you don't need it (with iso_tree_free).
 * 
 * @return 0 on success, -1 if the node doesn't belong to the dir.
 */
int iso_tree_node_take(struct iso_tree_node_dir *dir, 
                       struct iso_tree_node *node);

/**
 * Removes a child from a directory and free (unref) it.
 * If you want to keep the child alive, you need to iso_tree_node_ref() it
 * before this call, but in that case iso_tree_node_take() is a better
 * alternative.
 * 
 * @return 0 on success, -1 if the node doesn't belong to the dir (in this
 *         last case the node is not freed).
 */
int iso_tree_node_remove(struct iso_tree_node_dir *dir, 
                         struct iso_tree_node *node);

/**
 * Removes a child from a directory during an iteration, without freeing it.
 * It's like iso_tree_node_take(), but to be used during a directory 
 * iteration.
 * The node removed will be the last returned by the iteration.
 * 
 * The behavior on two call to this function without calling iso_tree_iter_next
 * between then is undefined, and should never occur. (TODO protect against this?)
 * 
 * @return 0 on success, < 0 on an invalid usage, i.e., if the user call this
 *         before an inicial iso_tree_iter_next() or if last 
 *         iso_tree_iter_next() has returned NULL.
 */
int iso_tree_node_take_iter(struct iso_tree_iter *iter);

/**
 * Removes a child from a directory during an iteration and free it.
 * It's like iso_tree_node_remove(), but to be used during a directory 
 * iteration.
 * The node removed will be the last returned by the iteration.
 * 
 * The behavior on two call to this function without calling iso_tree_iter_next
 * between then is undefined, and should never occur. (TODO protect against this?)
 * 
 * @return 0 on success, < 0 on an invalid usage, i.e., if the user call this
 *         before an inicial iso_tree_iter_next() or if last 
 *         iso_tree_iter_next() has returned NULL.
 */
int iso_tree_node_remove_iter(struct iso_tree_iter *iter);

/*
 * Get the parent of the given iso tree node.
 * This returns NULL if the node is the root of the tree, or is a node
 * that doesn't pertain to any tree (it was removed/take)
 */
struct iso_tree_node_dir *iso_tree_node_get_parent(struct iso_tree_node *node);

/**
 * Adds a child to a directory.
 * The child will be freed when the parent is freed, so you must be the
 * owner of the child (maybe calling iso_tree_node_ref) before calling this.
 * 
 * \pre parent has no child with the same name as \p child
 */
void iso_tree_add_child(struct iso_tree_node_dir *parent,
	                    struct iso_tree_node *child);

/**
 * Increments the reference counting of the given node.
 * If you call this, you must remember call iso_tree_free when the
 * node is no more needed.
 */
void iso_tree_node_ref(struct iso_tree_node *node);

/**
 * Recursively free a directory.
 *
 * \param root The root of the directory heirarchy to free.
 *
 * \pre \p root is non-NULL.
 */
void iso_tree_free(struct iso_tree_node *root);

/**
 * Recursively print a directory to stdout.
 * \param spaces The initial number of spaces on the left. Set to 0 if you
 *	supply a root directory.
 */
void iso_tree_print(const struct iso_tree_node *root, int spaces);

/** Create a burn_source which can be used as a data source for a track
 *
 * The volume set used to create the libburn_source can _not_ be modified
 * until the libburn_source is freed.
 *
 * \param volumeset The volume set from which you want to write
 * \param opts The options for image generation
 *
 * \pre \p volumeset is non-NULL
 * \pre \p volnum is less than \p volset->volset_size.
 * \return A burn_source to be used for the data source for a track
 */
struct burn_source* iso_source_new_ecma119(struct iso_volset *volumeset,
					    struct ecma119_source_opts *opts);

/**
 * Creates a new data source from the given file.
 * 
 * Returns NULL on error
 */
struct data_source *data_source_from_file(const char *path);

/** Free a given data source (decrease its refcount and maybe free it) */
void data_source_free(struct data_source*);

/**
 * Read an existing ISO image.
 * 
 * TODO documentar
 */
struct iso_volset *iso_volset_read(struct data_source *src, 
                                   struct ecma119_read_opts *opts);

/** 
 * Control queueing and stderr printing of messages from libisofs.
 * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
 * "NOTE", "UPDATE", "DEBUG", "ALL".
 * 
 * @param queue_severity Gives the minimum limit for messages to be queued.
 *                       Default: "NEVER". If you queue messages then you
 *                       must consume them by iso_msgs_obtain().
 * @param print_severity Does the same for messages to be printed directly
 *                       to stderr.
 * @param print_id       A text prefix to be printed before the message.
 * @return               >0 for success, <=0 for error
 */
int iso_msgs_set_severities(char *queue_severity,
                            char *print_severity, char *print_id);

#define ISO_MSGS_MESSAGE_LEN 4096

/** 
 * Obtain the oldest pending libisofs message from the queue which has at
 * least the given minimum_severity. This message and any older message of
 * lower severity will get discarded from the queue and is then lost forever.
 * 
 * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
 * "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER"
 * will discard the whole queue.
 * 
 * @param error_code Will become a unique error code as listed in messages.h
 * @param msg_text   Must provide at least ISO_MSGS_MESSAGE_LEN bytes.
 * @param os_errno   Will become the eventual errno related to the message
 * @param severity   Will become the severity related to the message and
 *                   should provide at least 80 bytes.
 * @return 1 if a matching item was found, 0 if not, <0 for severe errors
 */
int iso_msgs_obtain(char *minimum_severity,
                     int *error_code, char msg_text[], int *os_errno,
                     char severity[]);

/** Return the messenger object handle used by libisofs. This handle
    may be used by related libraries to replace their own compatible
    messenger objects and thus to direct their messages to the libisofs
    message queue. See also: libburn, API function burn_set_messenger().
    @return the handle. Do only use with compatible 
*/
void *iso_get_messenger(void);

#endif /* LIBISO_LIBISOFS_H */
