/********************************************************************
 * OBIDMS functions                                                 *
 ********************************************************************/

/**
 * @file obidms.c
 * @author Eric Coissac (eric.coissac@metabarcoding.org)
 * @date 23 May 2015
 * @brief OBIDMS functions.
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <libgen.h>   /* <EC> : Added July 28th 2017 to include basename */
#include <sys/mman.h>
#include <math.h>

#include "obidms.h"
#include "obierrno.h"
#include "obidebug.h"
#include "obidmscolumn.h"
#include "obiview.h"
#include "obiblob_indexer.h"
#include "utils.h"
#include "obilittlebigman.h"
#include "libjson/json_utils.h"
#include "obisig.h"


#define DEBUG_LEVEL 0	// TODO has to be defined somewhere else (cython compil flag?)


// Initialize global list of opened DMS and their associated counters
OBIDMS_p global_opened_dms_list[MAX_NB_OPENED_DMS+1] = { 0 };
int global_opened_dms_counter_list[MAX_NB_OPENED_DMS+1] = { 0 };



/**************************************************************************
 *
 * D E C L A R A T I O N   O F   T H E   P R I V A T E   F U N C T I O N S
 *
 **************************************************************************/


/**
 * Internal function building the OBIDMS directory name from an OBIDMS name.
 *
 * The function builds the directory name corresponding to an OBIDMS.
 * It also checks that the name is not too long.
 *
 * @warning The returned pointer has to be freed by the caller.
 *
 * @param dms_name The name of the OBIDMS.
 *
 * @returns A pointer to the directory name.
 * @retval NULL if an error occurred.
 *
 * @since May 2015
 * @author Eric Coissac (eric.coissac@metabarcoding.org)
 */
static char* build_directory_name(const char* dms_name);


/**
 * Internal function building the informations file name from an OBIDMS name.
 *
 * The function builds the file name for the informations file of an OBIDMS.
 *
 * @warning The returned pointer has to be freed by the caller.
 *
 * @param dms_name The name of the OBIDMS.
 *
 * @returns A pointer on the file name.
 * @retval NULL if an error occurred.
 *
 * @since November 2015
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static char* build_infos_file_name(const char* dms_name);


/**
 * Internal function calculating the initial size of the file where the informations about a DMS are stored.
 *
 * @returns The initial size of the file in bytes, rounded to a multiple of page size.
 *
 * @since September 2018
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static size_t get_platform_infos_file_size(void);


/**
 * @brief Internal function enlarging a DMS information file.
 *
 * @param dms A pointer on the DMS.
 * @param new_size The new size needed, in bytes (not necessarily rounded to a page size multiple).
 *
 * @retval 0 if the operation was successfully completed.
 * @retval -1 if an error occurred.
 *
 * @since September 2018
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static int enlarge_infos_file(OBIDMS_p dms, size_t new_size);


/**
 * @brief Internal function mapping a DMS information file and returning the mapped structure stored in it.
 *
 * The function checks that endianness of the platform and of the DMS match.
 *
 * @param dms_file_descriptor The file descriptor of the DMS directory.
 * @param dms_name The base name of the DMS.
 *
 * @returns A pointer on the mapped DMS infos structure.
 * @retval NULL if an error occurred.
 *
 * @since September 2018
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static OBIDMS_infos_p map_infos_file(int dms_file_descriptor, const char* dms_name);


/**
 * @brief Unmaps a DMS information file.
 *
 * @param dms A pointer on the OBIDMS.
 *
 * @returns A value indicating the success of the operation.
 * @retval 0 if the operation was successfully completed.
 * @retval -1 if an error occurred.
 *
 * @since October 2018
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static int unmap_infos_file(OBIDMS_p dms);


/**
 * Internal function creating the file containing basic informations on the OBIDMS.
 *
 * This file contains an OBIDMS_infos structure.
 *
 * @warning The returned pointer has to be freed by the caller.
 *
 * @param dms_file_descriptor The file descriptor of the DMS directory.
 * @param dms_name The base name of the DMS.
 *
 * @retval 0 if the operation was successfully completed.
 * @retval -1 if an error occurred.
 *
 * @since November 2015
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static int create_dms_infos_file(int dms_file_descriptor, const char* dms_name);


/**
 * Internal function adding a DMS in the global list of DMS opened by a program.
 *
 * @param dms A pointer on the DMS to add.
 *
 * @retval 0 if the operation was successfully completed.
 * @retval -1 if an error occurred.
 *
 * @since October 2017
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static int list_dms(OBIDMS_p dms);


/**
 * Internal function removing a DMS from the global list of DMS opened by a program.
 *
 * @param dms A pointer on the DMS to remove.
 * @param force Whether the DMS should be unlisted even if it is opened more than once.
 *
 * @retval 0 if the operation was successfully completed.
 * @retval -1 if the DMS was not found.
 *
 * @since October 2017
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static int unlist_dms(OBIDMS_p dms, bool force);


/**
 * Internal function checking if a DMS is already in the global list of DMS opened by the program.
 *
 * @param full_dms_path The absolute path to the DMS directory.
 *
 * @returns A pointer on the DMS if it is already opened, or NULL if it was not opened.
 *
 * @since November 2017
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static OBIDMS_p check_if_dms_in_list(const char* full_dms_path);


/**
 * Internal function returning the count for an opened DMS (how many times the DMS is opened) in the global list of DMS opened by the program.
 *
 * @param dms A pointer on the DMS.
 *
 * @returns The count indicating how many times the DMS has been opened by the program.
 *
 * @retval -1 if the DMS was not found.
 *
 * @since November 2017
 * @author Celine Mercier (celine.mercier@metabarcoding.org)
 */
static int dms_count_in_list(OBIDMS_p dms);



/************************************************************************
 *
 * D E F I N I T I O N   O F   T H E   P R I V A T E   F U N C T I O N S
 *
 ************************************************************************/

static char* build_directory_name(const char* dms_name)
{
	char* directory_name;

	// Build the database directory name
	directory_name = (char*) malloc((strlen(dms_name) + 8)*sizeof(char));
	if (sprintf(directory_name, "%s.obidms", dms_name) < 0)
	{
		obi_set_errno(OBIDMS_MEMORY_ERROR);
		obidebug(1, "\nProblem building an OBIDMS directory name");
		return NULL;
	}

	// Test if the database name is not too long
	if (strlen(directory_name) >= OBIDMS_MAX_NAME)
	{
		obi_set_errno(OBIDMS_LONG_NAME_ERROR);
		obidebug(1, "\nProblem building an OBIDMS directory name");
		free(directory_name);
		return NULL;
	}

	return directory_name;
}


static char* build_infos_file_name(const char* dms_name)
{
	char* file_name;

	// Build file name
	file_name = (char*) malloc((strlen(dms_name) + 7)*sizeof(char));
	if (sprintf(file_name, "%s_infos", dms_name) < 0)
	{
		obi_set_errno(OBIDMS_MEMORY_ERROR);
		obidebug(1, "\nProblem building an informations file name");
		return NULL;
	}

	return file_name;
}


static size_t get_platform_infos_file_size()
{
	size_t infos_size;
	size_t rounded_infos_size;
	double multiple;

	infos_size = sizeof(OBIDMS_infos_t);

	multiple = 	ceil((double) (infos_size) / (double) getpagesize());

	rounded_infos_size = multiple * getpagesize();

	return rounded_infos_size;
}


static int enlarge_infos_file(OBIDMS_p dms, size_t new_size)
{
	int    infos_file_descriptor;
	double multiple;
	size_t rounded_new_size;
	char*  file_name;

	// Create file name
	file_name = build_infos_file_name(dms->dms_name);
	if (file_name == NULL)
		return -1;

    // Open infos file
	infos_file_descriptor = openat(dms->dir_fd, file_name, O_RDWR, 0777);
	if (infos_file_descriptor < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError opening a DMS information file");
		free(file_name);
		return -1;
	}

	free(file_name);

	// Round new size to a multiple of page size	// TODO make function in utils
	multiple = 	ceil((double) new_size / (double) getpagesize());
	rounded_new_size = multiple * getpagesize();

	// Unmap the entire file before truncating it (WSL requirement)
	if (munmap(dms->infos, (dms->infos)->file_size) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError munmapping a DMS information file when enlarging");
		close(infos_file_descriptor);
		return -1;
	}

	// Enlarge the file
	if (ftruncate(infos_file_descriptor, rounded_new_size) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError enlarging a DMS information file");
		close(infos_file_descriptor);
		return -1;
	}

	// Remap the file
	dms->infos = mmap(NULL,
					  rounded_new_size,
					  PROT_READ | PROT_WRITE,
					  MAP_SHARED,
					  infos_file_descriptor,
					  0
					  );
	if (dms->infos == MAP_FAILED)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError re-mmapping a DMS information file after enlarging the file");
		close(infos_file_descriptor);
		return -1;
	}

	// Set new size
	(dms->infos)->file_size = rounded_new_size;

	if (close(infos_file_descriptor) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError closing a DMS information file");
		return -1;
	}

	return 0;
}


static OBIDMS_infos_p map_infos_file(int dms_file_descriptor, const char* dms_name)
{
	char*				file_name;
	OBIDMS_infos_p		dms_infos;
	int 				file_descriptor;
	size_t  			min_size;
	size_t  			full_size;
	bool                little_endian_platform;

	// Build file name
	file_name = build_infos_file_name(dms_name);
	if (file_name == NULL)
		return NULL;

    // Open file
	file_descriptor = openat(dms_file_descriptor,
							 file_name,
							 O_RDWR, 0777);
	if (file_descriptor < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError opening a DMS information file (DMS %s)", dms_name);
		free(file_name);
		return NULL;
	}

	free(file_name);

	// Map minimum size to read endianness and file size
	// (fread() fails to read properly from the structure in the file because of padding)
	min_size = get_platform_infos_file_size();

	dms_infos = mmap(NULL,
					 min_size,
					 PROT_READ | PROT_WRITE,
					 MAP_SHARED,
					 file_descriptor,
					 0
	   	   	 	 	);
	if (dms_infos == NULL)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError mapping a DMS information file");
		return NULL;
	}

	// Check endianness of the platform and DMS
	little_endian_platform = obi_is_little_endian();
	if (little_endian_platform != dms_infos->little_endian)
	{
		obi_set_errno(OBIDMS_BAD_ENDIAN_ERROR);
		obidebug(1, "\nError: The DMS and the platform have different endianness");
		close(file_descriptor);
		return NULL;
	}

	// Read actual file size
	full_size = dms_infos->file_size;

	// Unmap the minimum size and remap the file with the full size
	if (munmap(dms_infos, min_size) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError munmapping a DMS information file");
		close(file_descriptor);
		return NULL;
	}

	dms_infos = mmap(NULL,
				 	 full_size,
					 PROT_READ | PROT_WRITE,
					 MAP_SHARED,
					 file_descriptor,
					 0
	   	   	 	 	);
	if (dms_infos == NULL)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError mapping a DMS information file");
		return NULL;
	}

	if (close(file_descriptor) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError closing a DMS information file");
		return NULL;
	}

	return dms_infos;
}


static int unmap_infos_file(OBIDMS_p dms)
{
	OBIDMS_infos_p dms_infos;
	char* 	       file_name;
	int 	       file_descriptor;
	size_t         file_size;

	dms_infos = dms->infos;

	// Build file name
	file_name = build_infos_file_name(dms->dms_name);
	if (file_name == NULL)
		return -1;

    // Open file
	file_descriptor = openat(dms->dir_fd,
							 file_name,
							 O_RDWR, 0777);
	if (file_descriptor < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError opening a DMS information file (DMS %s) to unmap it", dms->dms_name);
		free(file_name);
		return -1;
	}

	free(file_name);

	// Unmap the DMS infos structure
	file_size = dms_infos->file_size;
	if (munmap(dms_infos, file_size) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError unmapping a DMS information file");
		close(file_descriptor);
		return -1;
	}

	if (close(file_descriptor) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError closing a DMS information file");
		return -1;
	}

	return 0;
}


static int create_dms_infos_file(int dms_file_descriptor, const char* dms_name)
{
	char*				file_name;
	int 				infos_file_descriptor;
	size_t  			file_size;

	// Build file name
	file_name = build_infos_file_name(dms_name);
	if (file_name == NULL)
		return -1;

    // Create file
	infos_file_descriptor = openat(dms_file_descriptor,
								   file_name,
								   O_RDWR | O_CREAT | O_EXCL,
								   0777);
	if (infos_file_descriptor < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError creating a DMS information file (DMS %s)", dms_name);
		free(file_name);
		return -1;
	}

	free(file_name);

	// Truncate file to the initial size
	file_size = get_platform_infos_file_size();

	if (ftruncate(infos_file_descriptor, file_size) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError truncating a DMS information file to the right size");
		close(infos_file_descriptor);
		return -1;
	}

	// Map the DMS infos structure
	OBIDMS_infos_p infos = mmap(NULL,
				 	 file_size,
					 PROT_READ | PROT_WRITE,
					 MAP_SHARED,
					 infos_file_descriptor,
					 0
	   	   	 	 	);

	// Initialize values
	infos->little_endian = obi_is_little_endian();
	infos->file_size = file_size;
	infos->used_size = 0;
	infos->working = false;
	infos->comments[0] = '\0';

	// Unmap the infos file
	if (munmap(infos, file_size) < 0)
	{
		obi_set_errno(OBIVIEW_ERROR);
		obidebug(1, "\nError munmapping a DMS information file");
		return -1;
	}

	if (close(infos_file_descriptor) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError closing a view file");
		return -1;
	}

	return 0;
}


static int list_dms(OBIDMS_p dms)
{
	int i = 0;
	while ((global_opened_dms_list[i] != NULL) && (global_opened_dms_list[i] != dms))
		i++;
	if (i == (MAX_NB_OPENED_DMS-1))
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError opening a DMS: maximum number of DMS opened by a program reached");
		return -1;
	}
	if (global_opened_dms_list[i] == NULL)
	{    // new dms
		global_opened_dms_list[i] = dms;
		global_opened_dms_counter_list[i] = 1;
	}
	else
	{    // already opened dms
		(global_opened_dms_counter_list[i])++;
	}

	return 0;
}


static int unlist_dms(OBIDMS_p dms, bool force)
{
	int i = 0;
	while ((global_opened_dms_list[i] != dms) && (i <= MAX_NB_OPENED_DMS))
		i++;
	if (i == MAX_NB_OPENED_DMS)
		return -1; // DMS not found
	// If opened more than once, and the unlisting is not forced, decrement counter
	if ((global_opened_dms_counter_list[i] > 1) && (! force))
		(global_opened_dms_counter_list[i])--;
	else
	{	// If opened once or forced unlisting, delete and unlist by shifting the list
		while (global_opened_dms_list[i] != NULL)
		{
			global_opened_dms_list[i] = global_opened_dms_list[i+1];
			i++;
		}
	}
	return 0;
}


static OBIDMS_p check_if_dms_in_list(const char* full_dms_path)
{
	int i = 0;
	while ((i <= MAX_NB_OPENED_DMS) && (global_opened_dms_list[i] != NULL) && (strcmp(global_opened_dms_list[i]->directory_path, full_dms_path) != 0))
		i++;
	if (i == MAX_NB_OPENED_DMS)
		return NULL; // DMS not found
	if (global_opened_dms_list[i] != NULL)
		return global_opened_dms_list[i];
	return NULL;
}


static int dms_count_in_list(OBIDMS_p dms)
{
	int i = 0;
	while ((i <= MAX_NB_OPENED_DMS) && (global_opened_dms_list[i] != dms))
		i++;
	if (i == MAX_NB_OPENED_DMS)
		return -1; // DMS not found
	return global_opened_dms_counter_list[i];
}


/**********************************************************************
 *
 * D E F I N I T I O N   O F   T H E   P U B L I C   F U N C T I O N S
 *
 **********************************************************************/

int obi_dms_is_clean(OBIDMS_p dms)
{
	int ret_val1;
	int ret_val2;

	ret_val1 = obi_dms_has_unfinished_views(dms);
	if (ret_val1 < 0)
		return -1;
	ret_val2 = obi_dms_has_unfinished_columns(dms);
	if (ret_val2 < 0)
		return -1;

	return !(ret_val1 || ret_val2);
}


int obi_clean_dms(const char* dms_path)
{
	OBIDMS_p dms;

	dms = obi_open_dms(dms_path, true);
	if (dms == NULL)
	{
		obidebug(1, "\nError opening a DMS before cleaning unfinished views and columns");
		return -1;
	}

	// Currently done in obi_open_dms
//	// Clean unfinished views
//	if (obi_clean_unfinished_views(dms) < 0)
//	{
//		obidebug(1, "\nError cleaning unfinished views");
//		return -1;
//	}
//
//	// Clean unfinished columns
//	if (obi_clean_unfinished_columns(dms) < 0)
//	{
//		obidebug(1, "\nError cleaning unfinished columns");
//		return -1;
//	}

	if (obi_close_dms(dms, true) < 0)
	{
		obidebug(1, "\nError closing a DMS after cleaning");
		return -1;
	}

	return 0;
}


int obi_dms_exists(const char* dms_path)
{
    struct stat buffer;
	char*       directory_name;
    int 		check_dir;

	// Build and check the directory name
    directory_name = build_directory_name(dms_path);
	if (directory_name == NULL)
		return -1;

	check_dir = stat(directory_name, &buffer);

	free(directory_name);

    if (check_dir == 0)
        return 1;
    else
    	return 0;
}


OBIDMS_p obi_create_dms(const char* dms_path)
{
	char*  			directory_name;
	DIR*  			dms_dir;
	int    			dms_file_descriptor;
	OBIDMS_p        dms;

	// Build and check the directory name
	directory_name = build_directory_name(dms_path);
	if (directory_name == NULL)
		return NULL;

	// Try to create the directory
	if (mkdir(directory_name, 00777) < 0)
	{
		if (errno == EEXIST)
		{
			obi_set_errno(OBIDMS_EXIST_ERROR);
			obidebug(1, "\nAn OBIDMS directory with the same name already exists in this directory.");
		}
		else
			obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nProblem creating an OBIDMS directory");
		free(directory_name);
		return NULL;
	}

	// Get file descriptor of DMS directory to create other directories
	dms_dir = opendir(directory_name);
	if (dms_dir == NULL)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nProblem opening a newly created OBIDMS directory");
		free(directory_name);
		return NULL;
	}

	free(directory_name);

	dms_file_descriptor = dirfd(dms_dir);
	if (dms_file_descriptor < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nProblem getting the file descriptor of a newly created OBIDMS directory");
		return NULL;
	}

	// Create the indexer directory
	if (mkdirat(dms_file_descriptor, INDEXER_DIR_NAME, 00777) < 0)
	{
		obi_set_errno(OBI_INDEXER_ERROR);
		obidebug(1, "\nProblem creating the indexer directory");
		return NULL;
	}

	// Create the view directory
	if (mkdirat(dms_file_descriptor, VIEW_DIR_NAME, 00777) < 0)
	{
		obi_set_errno(OBIVIEW_ERROR);
		obidebug(1, "\nProblem creating the view directory");
		return NULL;
	}

	// Create the taxonomy directory
	if (mkdirat(dms_file_descriptor, TAXONOMY_DIR_NAME, 00777) < 0)
	{
		obi_set_errno(OBIVIEW_ERROR);
		obidebug(1, "\nProblem creating the taxonomy directory");
		return NULL;
	}

	// Create the information file
	if (create_dms_infos_file(dms_file_descriptor, basename((char*)dms_path)) < 0)
		return NULL;

	// Open DMS
	dms = obi_open_dms(dms_path, false);
	if (dms == NULL)
	{
		obidebug(1, "\nProblem opening a DMS");
		return NULL;
	}

	// Write empty json string in comments (Python json lib doesn't like empty "")
	if (obi_dms_write_comments(dms, "{}") < 0)
	{
		obidebug(1, "\nProblem initializing empty json string in new DMS comments");
		return NULL;
	}

	if (closedir(dms_dir) < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError closing a directory");
		return NULL;
	}

	return dms;
}


OBIDMS_p obi_open_dms(const char* dms_path, bool cleaning)
{
	OBIDMS_p	dms;
	char*	 	relative_dms_path;
	char*	 	absolute_dms_path;
	//int			clean_dms;

	dms = NULL;

	// Build and check the directory name including the relative path
	relative_dms_path = build_directory_name(dms_path);
	if (relative_dms_path == NULL)
		return NULL;

	// Get and store the absolute path to the DMS directory
	absolute_dms_path = realpath(relative_dms_path, NULL);
	if (absolute_dms_path == NULL)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError getting the absolute path to the DMS directory (DMS does not exist)");
		free(relative_dms_path);
		return NULL;
	}

	free(relative_dms_path);

	// Check if the DMS is already opened
	dms = check_if_dms_in_list(absolute_dms_path);
	if (dms != NULL)
	{
		list_dms(dms);
		free(absolute_dms_path);
		return dms;
	}

	// Allocate the data structure
	dms = (OBIDMS_p) malloc(sizeof(OBIDMS_t));
	if (dms == NULL)
	{
		obi_set_errno(OBIDMS_MEMORY_ERROR);
		obidebug(1, "\nError allocating the memory for the OBIDMS structure");
		return NULL;
	}

	strcpy(dms->dms_name, basename((char*)dms_path));
	strcpy(dms->directory_path, absolute_dms_path);

	// Try to open the directory
	dms->directory = opendir(dms->directory_path);
	if (dms->directory == NULL)
	{
		switch (errno)
		{
			case ENOENT:
				obi_set_errno(OBIDMS_NOT_EXIST_ERROR);
				break;

			case EACCES:
				obi_set_errno(OBIDMS_ACCESS_ERROR);
				break;

			case ENOMEM:
				obi_set_errno(OBIDMS_MEMORY_ERROR);
				break;

			default:
				obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		}
		obidebug(1, "\nCan't open OBIDMS directory");
		free(dms);
		return NULL;
	}

	// Get and store file descriptor of DMS directory
	dms->dir_fd = dirfd(dms->directory);
	if (dms->dir_fd < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError getting the file descriptor for a newly created OBIDMS directory");
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Open the information file
	dms->infos = map_infos_file(dms->dir_fd, dms->dms_name);
	if (dms->infos == NULL)
	{
		obidebug(1, "\nError opening a DMS information file");
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Reset the working variable to false if cleaning the DMS
	if (cleaning)
		(dms->infos)->working = false;

	// Check that the DMS is not already working (being used by a process)
	if ((dms->infos)->working)
	{
		obidebug(1, "\n\nERROR:\nThe DMS '%s' contains unfinished views or columns. Either another command is currently running, "
                    "in which case you have to wait for it to finish, or a previous command was interrupted, "
                    "in which case you can run 'obi clean_dms [your_dms]' to clean the DMS.\n", dms->dms_name);
		obi_set_errno(OBIDMS_WORKING);
		unmap_infos_file(dms);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Set the working variable to true
	(dms->infos)->working = true;

	// Open the indexer directory
	dms->indexer_directory = opendir_in_dms(dms, INDEXER_DIR_NAME);
	if (dms->indexer_directory == NULL)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError opening the indexer directory");
		unmap_infos_file(dms);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Store the indexer directory's file descriptor
	dms->indexer_dir_fd = dirfd(dms->indexer_directory);
	if (dms->indexer_dir_fd < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError getting the file descriptor of the indexer directory");
		unmap_infos_file(dms);
		closedir(dms->indexer_directory);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Open the view directory
	dms->view_directory = opendir_in_dms(dms, VIEW_DIR_NAME);
	if (dms->view_directory == NULL)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError opening the view directory");
		unmap_infos_file(dms);
		closedir(dms->indexer_directory);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Store the view directory's file descriptor
	dms->view_dir_fd = dirfd(dms->view_directory);
	if (dms->view_dir_fd < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError getting the file descriptor of the view directory");
		unmap_infos_file(dms);
		closedir(dms->view_directory);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Open the taxonomy directory
	dms->tax_directory = opendir_in_dms(dms, TAXONOMY_DIR_NAME);
	if (dms->tax_directory == NULL)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError opening the taxonomy directory");
		unmap_infos_file(dms);
		closedir(dms->indexer_directory);
		closedir(dms->view_directory);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Store the taxonomy directory's file descriptor
	dms->tax_dir_fd = dirfd(dms->tax_directory);
	if (dms->tax_dir_fd < 0)
	{
		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
		obidebug(1, "\nError getting the file descriptor of the taxonomy directory");
		unmap_infos_file(dms);
		closedir(dms->indexer_directory);
		closedir(dms->tax_directory);
		closedir(dms->view_directory);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	//	// Check for unfinished views and columns
	//	clean_dms = obi_dms_is_clean(dms);
	//	if (clean_dms < 0)
	//	{
	//		obi_set_errno(OBIDMS_UNKNOWN_ERROR);
	//		obidebug(1, "\nError checking if a DMS has unfinished views or columns when opening it");
	//		unmap_infos_file(dms);
	//		closedir(dms->indexer_directory);
	//		closedir(dms->tax_directory);
	//		closedir(dms->view_directory);
	//		closedir(dms->directory);
	//		free(dms);
	//		return NULL;
	//	}
	//	if (! clean_dms)
	//		obi_set_errno(OBIDMS_NOT_CLEAN);

	// Clean unfinished views
	if (obi_clean_unfinished_views(dms) < 0)
	{
		obidebug(1, "\nError cleaning unfinished views when opening an OBIDMS");
		unmap_infos_file(dms);
		closedir(dms->indexer_directory);
		closedir(dms->tax_directory);
		closedir(dms->view_directory);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Clean unfinished columns
	if (obi_clean_unfinished_columns(dms) < 0)
	{
		obidebug(1, "\nError cleaning unfinished columns when opening an OBIDMS");
		unmap_infos_file(dms);
		closedir(dms->indexer_directory);
		closedir(dms->tax_directory);
		closedir(dms->view_directory);
		closedir(dms->directory);
		free(dms);
		return NULL;
	}

	// Initialize the list of opened columns
	dms->opened_columns = (Opened_columns_list_p) malloc(sizeof(Opened_columns_list_t));
	(dms->opened_columns)->nb_opened_columns = 0;

	// Initialize the list of opened indexers
	dms->opened_indexers = (Opened_indexers_list_p) malloc(sizeof(Opened_indexers_list_t));
	(dms->opened_indexers)->nb_opened_indexers = 0;

	// Add in the global list of opened DMS
	if (list_dms(dms) < 0)
	{
		obidebug(1, "\nError cleaning unfinished columns when opening an OBIDMS");
		unmap_infos_file(dms);
		closedir(dms->indexer_directory);
		closedir(dms->tax_directory);
		closedir(dms->view_directory);
		closedir(dms->directory);
		free(dms->opened_columns);
		free(dms->opened_indexers);
		free(dms);
		return NULL;
	}

	return dms;
}


OBIDMS_p obi_test_open_dms(const char* dms_name)
{
	int exists;

	exists = obi_dms_exists(dms_name);

	switch (exists)
	{
		case 0:
			return NULL;
		case 1:
			return obi_open_dms(dms_name, false);
	};

	obidebug(1, "\nError checking if an OBIDMS directory exists");
	return NULL;
}


OBIDMS_p obi_dms(const char* dms_name)
{
	int exists;

	exists = obi_dms_exists(dms_name);

	switch (exists)
	{
		case 0:
			return obi_create_dms(dms_name);
		case 1:
			return obi_open_dms(dms_name, false);
	};

	obidebug(1, "\nError checking if an OBIDMS directory exists");
	return NULL;
}


int obi_close_dms(OBIDMS_p dms, bool force)
{
	int dms_counter;

	if (!force)
	{
		dms_counter = dms_count_in_list(dms);
		if (dms_counter < 0)
			obidebug(1, "\nError checking the counter of an OBIDMS in the global list of opened OBIDMS");

		if (dms_counter > 1)	// Don't close if the DMS is opened more than once
		{
			if (unlist_dms(dms, force) < 0)
			{
				obidebug(1, "\nError decrementing the counter of an OBIDMS in the global list of opened OBIDMS");
				return -1;
			}
			return 0;
		}
	}

	if (dms != NULL)
	{
		// Close all columns
		while ((dms->opened_columns)->nb_opened_columns > 0)
			obi_close_column(*((dms->opened_columns)->columns));

		// Close dms, and view, indexer and taxonomy directories
		if (closedir(dms->indexer_directory) < 0)
		{
			obi_set_errno(OBI_INDEXER_ERROR);
			obidebug(1, "\nError closing an indexer directory");
			free(dms);
			return -1;
		}
		if (closedir(dms->view_directory) < 0)
		{
			obi_set_errno(OBIVIEW_ERROR);
			obidebug(1, "\nError closing a view directory");
			free(dms);
			return -1;
		}
		if (closedir(dms->tax_directory) < 0)
		{
			obi_set_errno(OBIVIEW_ERROR);
			obidebug(1, "\nError closing a taxonomy directory");
			free(dms);
			return -1;
		}

		// Set the working variable as false
		(dms->infos)->working = false;

		// Unmap information file
		if (unmap_infos_file(dms) < 0)
		{
			obidebug(1, "\nError unmaping a DMS information file while closing a DMS");
			free(dms);
			return -1;
		}

		if (closedir(dms->directory) < 0)
		{
			obi_set_errno(OBIDMS_MEMORY_ERROR);
			obidebug(1, "\nError closing an OBIDMS directory");
			free(dms);
			return -1;
		}

		// Remove DMS from global list of opened DMS
		if (unlist_dms(dms, force) < 0)
		{
			obidebug(1, "\nError removing an OBIDMS from the global list of opened OBIDMS when closing it");
			free(dms);
			return -1;
		}

		free(dms);
	}

	return 0;
}


int obi_dms_write_comments(OBIDMS_p dms, const char* comments)
{
	size_t new_size;

	if (comments == NULL)
		return 0;	// TODO or error? discuss

	new_size = sizeof(OBIDMS_infos_t) + strlen(comments) + 1;

	// Check if the file has to be enlarged
	if (new_size >= (dms->infos)->file_size)
	{
		if (enlarge_infos_file(dms, new_size) < 0)
			return -1;
	}

	strcpy((dms->infos)->comments, comments);

	(dms->infos)->used_size = new_size;

	return 0;
}


int obi_dms_add_comment(OBIDMS_p dms, const char* key, const char* value)
{
	char* new_comments = NULL;

	new_comments = obi_add_comment((dms->infos)->comments, key, value);
	if (new_comments == NULL)
	{
		obidebug(1, "\nError adding a comment to a dms, key: %s, value: %s", key, value);
		return -1;
	}

	if (obi_dms_write_comments(dms, new_comments) < 0)
	{
		obidebug(1, "\nError adding a comment to a dms, key: %s, value: %s", key, value);
		return -1;
	}

	free(new_comments);

	return 0;
}


OBIDMS_column_p obi_dms_get_column_from_list(OBIDMS_p dms, const char* column_name, obiversion_t version)
{
	int i;

	for (i=0; i < ((dms->opened_columns)->nb_opened_columns); i++)
	{
		if (!strcmp(((*(((dms->opened_columns)->columns)+i))->header)->name, column_name)
				&& (((*(((dms->opened_columns)->columns)+i))->header)->version == version))
		{	// Found the column already opened, return it
			return *(((dms->opened_columns)->columns)+i);
		}
	}
	// Didn't find the column
	return NULL;
}


void obi_dms_list_column(OBIDMS_p dms, OBIDMS_column_p column)	// TODO add check if column already in list?
{
	*(((dms->opened_columns)->columns)+((dms->opened_columns)->nb_opened_columns)) = column;
	((dms->opened_columns)->nb_opened_columns)++;
}


int obi_dms_unlist_column(OBIDMS_p dms, OBIDMS_column_p column)
{
	int i;
	Opened_columns_list_p columns_list;

	columns_list = dms->opened_columns;

	for (i=0; i < columns_list->nb_opened_columns; i++)
	{
		if (!strcmp(((*((columns_list->columns)+i))->header)->name, (column->header)->name)
				&& (((*((columns_list->columns)+i))->header)->version == (column->header)->version))
		{	// Found the column. Rearrange list
			(columns_list->nb_opened_columns)--;
			(columns_list->columns)[i] = (columns_list->columns)[columns_list->nb_opened_columns];
			return 0;
		}
	}

	obidebug(1, "\nCould not find the column to delete from list of open columns");
	return -1;
}


Obi_indexer_p obi_dms_get_indexer_from_list(OBIDMS_p dms, const char* indexer_name)
{
	int i;
	Opened_indexers_list_p indexers_list;

	indexers_list = dms->opened_indexers;

	for (i=0; i < (indexers_list->nb_opened_indexers); i++)
	{
		if (!strcmp(obi_indexer_get_name((indexers_list->indexers)[i]), indexer_name))
		{	// Found the indexer already opened, return it
			return (indexers_list->indexers)[i];
		}
	}
	// Didn't find the indexer
	return NULL;
}


void obi_dms_list_indexer(OBIDMS_p dms, Obi_indexer_p indexer)  	// TODO add check if indexer already in list?
{
	*(((dms->opened_indexers)->indexers)+((dms->opened_indexers)->nb_opened_indexers)) = indexer;
	((dms->opened_indexers)->nb_opened_indexers)++;
}


int obi_dms_unlist_indexer(OBIDMS_p dms, Obi_indexer_p indexer)
{
	int i;
	Opened_indexers_list_p indexers_list;

	indexers_list = dms->opened_indexers;

	for (i=0; i < indexers_list->nb_opened_indexers; i++)
	{
		if (!strcmp(obi_indexer_get_name((indexers_list->indexers)[i]), indexer->name))
		{	// Found the indexer. Rearrange list
			(indexers_list->nb_opened_indexers)--;
			(indexers_list->indexers)[i] = (indexers_list->indexers)[indexers_list->nb_opened_indexers];
			return 0;
		}
	}

	obidebug(1, "\nCould not find the indexer to delete from list of open indexers");
	return -1;
}


char* obi_dms_get_dms_path(OBIDMS_p dms)
{
	char* full_path;

	full_path = (char*) malloc((MAX_PATH_LEN)*sizeof(char));
	if (full_path == NULL)
	{
		obi_set_errno(OBI_MALLOC_ERROR);
		obidebug(1, "\nError allocating memory for the char* path to a file or directory");
		return NULL;
	}

	strcpy(full_path, dms->directory_path);

	return full_path;
}


char* obi_dms_get_full_path(OBIDMS_p dms, const char* path_name)
{
	char* full_path;

	full_path = obi_dms_get_dms_path(dms);
	strcat(full_path, "/");
	strcat(full_path, path_name);

	return full_path;
}


DIR* opendir_in_dms(OBIDMS_p dms, const char* path_name)
{
	char* full_path;
	DIR* directory;

	full_path = obi_dms_get_full_path(dms, path_name);
	if (full_path == NULL)
		return NULL;

	directory = opendir(full_path);
	if (directory == NULL)
	{
		obi_set_errno(OBI_UTILS_ERROR);
		obidebug(1, "\nError opening a directory");
	}

	free(full_path);

	return directory;
}


char* obi_dms_formatted_infos(OBIDMS_p dms, bool detailed)
{
	char*          dms_infos = NULL;
	char*   	   view_infos = NULL;
	char*          view_name = NULL;
	char*          tax_name = NULL;
	char*          all_tax_dir_path = NULL;
	int            i, last_dot_pos;
	struct dirent* dp;
	Obiview_p      view;

	// DMS name
	dms_infos = (char*) malloc((strlen("# DMS name: ")+strlen(dms->dms_name)+strlen("\n# Views:\n")+1) * sizeof(char));
	if (dms_infos == NULL)
	{
		obidebug(1, "\nError allocating memory for DMS formatted infos");
		return NULL;
	}
	strcpy(dms_infos, "# DMS name: ");
	strcat(dms_infos, dms->dms_name);
	strcat(dms_infos, "\n# Views:\n");

	// Go through views and get their infos
	rewinddir(dms->view_directory);
	while ((dp = readdir(dms->view_directory)) != NULL)
	{
		if ((dp->d_name)[0] == '.')
			continue;
		i=0;
		while (i < strlen(dp->d_name))
		{
			if ((dp->d_name)[i] == '.')
				last_dot_pos = i;
			i++;
		}
		view_name = (char*) malloc((last_dot_pos+1) * sizeof(char));
		if (view_name == NULL)
		{
			obi_set_errno(OBI_MALLOC_ERROR);
			obidebug(1, "\nError allocating memory for a view name when getting formatted DMS infos: file %s", dp->d_name);
			return NULL;
		}
		strncpy(view_name, dp->d_name, last_dot_pos);
		view_name[last_dot_pos] = '\0';
		view = obi_open_view(dms, view_name);
		if (view == NULL)
		{
			obidebug(1, "\nError opening a view to get DMS formatted infos");
			return NULL;
		}
		if (detailed)
			view_infos = obi_view_formatted_infos(view, detailed);
		else
			view_infos = obi_view_formatted_infos_one_line(view);
		if (view_infos == NULL)
		{
			obidebug(1, "\nError getting a view infos to get DMS formatted infos");
			return NULL;
		}
		dms_infos = realloc(dms_infos, (strlen(dms_infos)+strlen(view_infos)+1) * sizeof(char));
		if (dms_infos == NULL)
		{
			obidebug(1, "\nError reallocating memory for DMS formatted infos");
			return NULL;
		}
		strcat(dms_infos, view_infos);
		if (obi_save_and_close_view(view) < 0)
		{
			obidebug(1, "\nError closing view while getting DMS formatted infos");
			return NULL;
		}
		if (detailed)
		{
			dms_infos = realloc(dms_infos, (strlen(dms_infos)+2) * sizeof(char));
			strcat(dms_infos, "\n");
		}
	}

	// Add taxonomies
	dms_infos = realloc(dms_infos, (strlen(dms_infos)+strlen("\n# Taxonomies:\n")+1) * sizeof(char));
	if (dms_infos == NULL)
	{
		obidebug(1, "\nError reallocating memory for DMS formatted infos");
		return NULL;
	}
	strcat(dms_infos, "# Taxonomies:\n");
	rewinddir(dms->tax_directory);
	while ((dp = readdir(dms->tax_directory)) != NULL)
	{
		if ((dp->d_name)[0] == '.')
			continue;
		tax_name = dp->d_name;
		dms_infos = realloc(dms_infos, (strlen(dms_infos)+strlen("  # ")+strlen(view_infos)+1) * sizeof(char));
		if (dms_infos == NULL)
		{
			obidebug(1, "\nError reallocating memory for DMS formatted infos");
			return NULL;
		}
		strcat(dms_infos, "  # ");
		strcat(dms_infos, tax_name);
	}
	return dms_infos;
}


// TODO move somewhere else maybe
// TODO discuss arguments
obiversion_t obi_import_column(const char* dms_path_1, const char* dms_path_2, const char* column_name, obiversion_t version_number)
{
	OBIDMS_p				dms_1;
	OBIDMS_p 				dms_2;
	OBIDMS_column_p 		column_1;
	OBIDMS_column_p 	   	column_2;
	OBIDMS_column_header_p 	header_1;
	OBIDMS_column_header_p 	header_2;
	int 					avl_exists;
	const char* 			avl_name;
	char* 					new_avl_name;
	obiversion_t 			new_version;
	int 					i;
	int 					avl_count;
	char*		 			avl_name_1;
	char* 					avl_name_2;
	char* 					avl_file_path_1;
	char* 					avl_file_path_2;
	char* 					avl_data_file_path_1;
	char* 					avl_data_file_path_2;
	char* 					complete_avl_name;
	Obi_indexer_p 			avl_group;

	dms_1 = obi_open_dms(dms_path_1, false);
	if (dms_1 == NULL)
	{
		obidebug(1, "\nError opening a DMS to import a column from it");
		return -1;
	}

	dms_2 = obi_dms(dms_path_2);
	if (dms_2 == NULL)
	{
		obidebug(1, "\nError opening or creating a DMS to import a column into it");
		return -1;
	}

	column_1 = obi_open_column(dms_1, column_name, version_number);
	if (column_1 == NULL)
	{
		obidebug(1, "\nError opening a column to import in another DMS");
		return -1;
	}

	header_1 = column_1->header;

	// Check if associated indexer exists BEFORE creating the new column as that will automatically create it if it doesn't already exist
	avl_name = header_1->indexer_name;
	avl_exists = obi_indexer_exists(dms_2, avl_name);
	if (avl_exists == -1)
	{
		obidebug(1, "\nError checking if an indexer exists while importing a column");
		return -1;
	}
	if (avl_exists)
		// Use automatic name
		new_avl_name = NULL;
	else
		// Name can stay the same
		new_avl_name = header_1->indexer_name;

	// Create new column
	column_2 = obi_create_column(dms_2, column_name, header_1->returned_data_type, header_1->line_count,
								 header_1->nb_elements_per_line, header_1->elements_names, true, header_1->dict_column,
								 header_1->tuples, header_1->to_eval, new_avl_name, (header_1->associated_column).column_name,
								 (header_1->associated_column).version, header_1->comments);

	if (column_2 == NULL)
	{
		obidebug(1, "\nError creating the new column while importing a column");
		return -1;
	}

	header_2 = column_2->header;

	// Get the new version to return
	new_version = header_2->version;

	// Copy lines_used informations
	header_2->lines_used = header_1->lines_used;

	// Copy data TODO check how much time and memory that costs, eventually use write() instead
	memcpy(column_2->data, column_1->data, header_1->data_size);

	// Copy the AVL files if there are some (overwriting the automatically created files)
	if ((header_1->tuples) || ((header_1->returned_data_type == OBI_STR) || (header_1->returned_data_type == OBI_SEQ) || (header_1->returned_data_type == OBI_QUAL)))
	{
		avl_name_1 = (char*) malloc((strlen(header_1->indexer_name) + 1)  * sizeof(char));
		if (avl_name_1 == NULL)
		{
			obi_set_errno(OBI_MALLOC_ERROR);
			obidebug(1, "\nError allocating memory for an AVL name when importing a column");
			return -1;
		}
		strcpy(avl_name_1, header_1->indexer_name);

		avl_name_2 = (char*) malloc((strlen(header_2->indexer_name) + 1)  * sizeof(char));
		if (avl_name_2 == NULL)
		{
			obi_set_errno(OBI_MALLOC_ERROR);
			obidebug(1, "\nError allocating memory for an AVL name when importing a column");
			return -1;
		}
		strcpy(avl_name_2, header_2->indexer_name);

		avl_count = (column_1->indexer->last_avl_idx) + 1;

		// Close column to manipulate AVL files safely (but not multithreading safe) (TODO not sure how important this is, can't find informations about conflicts when using write() on mmapped files)
		if (obi_close_column(column_1) < 0)
		{
			obidebug(1, "\nError closing an imported column");
			return -1;
		}

		(column_2->header)->finished = true;  // note: this is normally handled by the view that created the column
		if (obi_close_column(column_2) < 0)
		{
			obidebug(1, "\nError closing an imported column");
			return -1;
		}

		for (i=0; i < avl_count; i++)
		{
			avl_file_path_1 = obi_get_full_path_of_avl_file_name(dms_1, avl_name_1, i);
			if (avl_file_path_1 == NULL)
			{
				obidebug(1, "\nError getting an AVL file path while importing a column");
				return -1;
			}

			avl_data_file_path_1 = obi_get_full_path_of_avl_data_file_name(dms_1, avl_name_1, i);
			if (avl_data_file_path_1 == NULL)
			{
				obidebug(1, "\nError getting an AVL file path while importing a column");
				return -1;
			}
			avl_file_path_2 = obi_get_full_path_of_avl_file_name(dms_2, avl_name_2, i);
			if (avl_file_path_2 == NULL)
			{
				obidebug(1, "\nError getting an AVL file path while importing a column");
				return -1;
			}
			avl_data_file_path_2 = obi_get_full_path_of_avl_data_file_name(dms_2, avl_name_2, i);
			if (avl_data_file_path_2 == NULL)
			{
				obidebug(1, "\nError getting an AVL file path while importing a column");
				return -1;
			}

			// Copy AVL file
			if (copy_file(avl_file_path_1, avl_file_path_2) < 0)
			{
				obidebug(1, "\nError copying an AVL file while importing a column");
				return -1;
			}

			// Copy AVL data file
			if (copy_file(avl_data_file_path_1, avl_data_file_path_2) < 0)
			{
				obidebug(1, "\nError copying a data AVL file while importing a column");
				return -1;
			}

			free(avl_file_path_1);
			free(avl_file_path_2);
			free(avl_data_file_path_1);
			free(avl_data_file_path_2);
		}

		// Update AVL names in headers
		avl_group = obi_open_indexer(dms_2, avl_name_2);
		for (i=0; i < avl_count; i++)
		{
			complete_avl_name = obi_build_avl_name_with_idx(avl_name_2, i);
			strcpy((((avl_group->sub_avls)[i])->header)->avl_name, complete_avl_name);
			strcpy(((((avl_group->sub_avls)[i])->data)->header)->avl_name, complete_avl_name);
			free(complete_avl_name);
		}

		free(avl_name_1);
		free(avl_name_2);
	}
	else
	{
		if (obi_close_column(column_1) < 0)
		{
			obidebug(1, "\nError closing an imported column");
			return -1;
		}

		(column_2->header)->finished = true;  // note: this is normally handled by the view that created the column
		if (obi_close_column(column_2) < 0)
		{
			obidebug(1, "\nError closing an imported column");
			return -1;
		}
	}

	// Copy associated column (update version)
	//new_associated_col_version = import_column(dms_path_1, dms_path_2, header_1->associated_column_name, header_1->associated_column_version);
	// TODO no? because if iterating over all columns in a view etc.... iterate and change associated columns version refs afterwards?

	// Close the DMS
	obi_close_dms(dms_1, false);
	obi_close_dms(dms_2, false);

	return new_version;
}


// TODO move somewhere else maybe
// TODO discuss arguments
int obi_import_view(const char* dms_path_1, const char* dms_path_2, const char* view_name_1, const char* view_name_2)
{
	OBIDMS_p 		dms_1;
	OBIDMS_p	 	dms_2;
	Obiview_p 		view_1;
	Obiview_p 		view_2;
	obiversion_t 	new_version;
	int 			i, j;
	OBIDMS_column_header_p header = NULL;
	OBIDMS_column_header_p header_2 = NULL;

	signal(SIGINT, sig_handler);

	dms_1 = obi_open_dms(dms_path_1, false);
	if (dms_1 == NULL)
	{
		obidebug(1, "\nError opening a DMS to import a view from it");
		return -1;
	}

	dms_2 = obi_dms(dms_path_2);
	if (dms_2 == NULL)
	{
		obidebug(1, "\nError opening or creating a DMS to import a view into it");
		return -1;
	}

	// Open view to import
	view_1 = obi_open_view(dms_1, view_name_1);

	// Create new view
	if (strcmp((view_1->infos)->view_type, VIEW_TYPE_NUC_SEQS) == 0)
		view_2 = obi_new_view_nuc_seqs(dms_2, view_name_2, NULL, NULL, (view_1->infos)->comments, false, false);
	else	// Non-typed view
		view_2 = obi_new_view(dms_2, view_name_2, NULL, NULL, (view_1->infos)->comments);

	if (view_2 == NULL)
	{
		obidebug(1, "\nError creating the new view to import a view in a DMS");
		return -1;
	}

	// Import line count
	view_2->infos->line_count = view_1->infos->line_count;

	// Import the line selection column if there is one
	if (! view_1->infos->all_lines)
	{
		view_2->infos->all_lines = false;
		new_version = obi_import_column(dms_path_1, dms_path_2, ((view_1->infos)->line_selection).column_name, ((view_1->infos)->line_selection).version);
		if (new_version == -1)
		{
			obidebug(1, "\nError importing a line selection column while importing a view");
			return -1;
		}
		strcpy(((view_2->infos)->line_selection).column_name, ((view_1->infos)->line_selection).column_name);
		((view_2->infos)->line_selection).version = new_version;
		view_2->line_selection = obi_open_column(dms_2, ((view_2->infos)->line_selection).column_name, ((view_2->infos)->line_selection).version);
		if (view_2->line_selection == NULL)
		{
			obidebug(1, "\nError opening a line selection column while importing a view");
			return -1;
		}
	}

	// Import each column and update with the new version number
	for (i=0; i < (view_1->infos->column_count); i++)
	{
		if (! keep_running)
			return -1;

		new_version = obi_import_column(dms_path_1, dms_path_2, ((((view_1->infos)->column_references)[i]).column_refs).column_name, ((((view_1->infos)->column_references)[i]).column_refs).version);
		if (new_version == -1)
		{
			obidebug(1, "\nError importing a column while importing a view");
			return -1;
		}

		if (obi_view_add_column(view_2,
							    ((((view_1->infos)->column_references)[i]).column_refs).column_name,
							    new_version,
							    (((view_1->infos)->column_references)[i]).alias,
							    0,
								0,
								0,
								NULL,
								false,
								false,
								false,
								false,
								NULL,
								NULL,
								-1,
								NULL,
								false) < 0)
		{
			obidebug(1, "\nError adding a column to a view while importing it");
			return -1;
		}
	}

	// Go through columns again to update associated columns
	for (i=0; i < (view_1->infos->column_count); i++)
	{
		if (! keep_running)
			return -1;

		header = obi_column_get_header_from_name(dms_1, ((((view_1->infos)->column_references)[i]).column_refs).column_name, ((((view_1->infos)->column_references)[i]).column_refs).version);
		if (header == NULL)
		{
			obidebug(1, "\nError opening a column header while importing a view");
			return -1;
		}

		if (header->returned_data_type == OBI_QUAL)   // TODO maybe there should be a bool in column headers
		{
			// Look for the index of the associated column in the list
			for (j=0; j < (view_1->infos->column_count); j++)
			{
				if ((strcmp((header->associated_column).column_name, ((((view_1->infos)->column_references)[j]).column_refs).column_name) == 0) &&
						((header->associated_column).version == ((((view_1->infos)->column_references)[j]).column_refs).version))
					break;
			}

			header_2 = obi_column_get_header_from_name(dms_2, ((((view_2->infos)->column_references)[i]).column_refs).column_name, ((((view_2->infos)->column_references)[i]).column_refs).version);
			if (header_2 == NULL)
			{
				obidebug(1, "\nError opening a column header while importing a view");
				return -1;
			}

			// Update version of associated column
			(header_2->associated_column).version = ((((view_2->infos)->column_references)[j]).column_refs).version;
			if (obi_close_header(header_2) < 0)
			{
				obidebug(1, "\nError closing a column header while importing a view");
				return -1;
			}
		}
		if (obi_close_header(header) < 0)
		{
			obidebug(1, "\nError closing a column header while importing a view");
			return -1;
		}
	}

	if (! keep_running)
		return -1;

	// Close the views
	if (obi_save_and_close_view(view_1) < 0)
	{
		obidebug(1, "\nError closing a view after importing from it");
		return -1;
	}
	if (obi_save_and_close_view(view_2) < 0)
	{
		obidebug(1, "\nError closing a view after importing it");
		return -1;
	}

	// Close the DMS
	obi_close_dms(dms_1, false);
	obi_close_dms(dms_2, false);

	return 0;
}


void obi_close_atexit()
{
	int i=0;

	while (global_opened_dms_list[i] != NULL)
	{
        obi_close_dms(global_opened_dms_list[i], true);
		i++;
	}
}

