/* -*- 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 <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.
 * \see tree.h
 */
struct iso_tree_node;

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

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

/**
 * 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
};

enum ecma119_relaxed_constraints_flag {
	ECMA119_OMIT_VERSION_NUMBERS = (1<<0),
	/* 37 char filenames involves no version number */
	ECMA119_37_CHAR_FILENAMES = (1<<1) | (1<<0), 
	ECMA119_NO_DIR_REALOCATION = (1<<2),
	ECMA119_RELAXED_FILENAMES = (1<<3)
};

/**
 * 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 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 */
};

/**
 * 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

/**
 * 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;
};

/**
 * 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);

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

/**
 * 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);

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

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

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * Create a bootable volume by adding a El-Torito boot 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 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_create_boot_catalog(struct iso_volume *volume, 
                               struct iso_tree_node *image,
                               enum eltorito_boot_media_type type,
                               struct iso_tree_node_dir *dir, 
                               char *name);

/**
 * 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.
 * Be aware that libisofs will modify original boot image file, so do a backup
 * if needed.
 * This is needed for isolinux boot images.
 */
void
el_torito_set_write_boot_info(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.
 *
 * TODO we need a way to allow developers know which kind of node is.
 * Think about this when designing the read api 
 */
struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

/**
 * 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);

#endif /* LIBISO_LIBISOFS_H */
