#ifndef DIR_H
#define DIR_H

#include <stddef.h>

enum dir_err {
	DIR_ERR_OK = 0,
	DIR_ERR_FAIL_TO_OPEN_DIR,
	DIR_ERR_PATH_IS_NOT_DIR,
	DIR_ERR_PATH_DOES_NOT_EXIST,
	DIR_ERR_FAIL_READ_DIR,
};

const char** list_dir(const char *path, size_t *size, enum dir_err *err);
void list_dir_destroy(const char **files);

void dir_err_set(enum dir_err *err, enum dir_err err_);


#if defined(DIR_IMP) || defined(IMP)

#include <errno.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>

#include "da.h"

const char**
list_dir(const char *path, size_t *size, enum dir_err *err)
{
	DIR *dir = opendir(path);
	if ( dir == NULL ) {
		dir_err_set(err, DIR_ERR_FAIL_TO_OPEN_DIR);
		if ( errno == ENOTDIR ) {
			dir_err_set(err, DIR_ERR_PATH_IS_NOT_DIR);
		} else if ( errno == ENOENT ) {
			dir_err_set(err, DIR_ERR_PATH_DOES_NOT_EXIST);
		}
		return NULL;
	}
	DA_DEF_STRUCT_ITEM(const char *, files);
	DA_CREATE(files);

	struct dirent *d = readdir(dir);
	while ( d != NULL ) {
		DA_APPEND(files, malloc(256));
		memset((char *)DA_TAIL(files), 0, 256);
		memcpy((char *)DA_TAIL(files), d->d_name, 256);
		d = readdir(dir);
	}
	if ( errno != 0 ) {
		list_dir_destroy(files.items);
		dir_err_set(err, DIR_ERR_FAIL_READ_DIR);
		closedir(dir);
		return NULL;
	}
	closedir(dir);

	if ( size != NULL ) {
		*size = files.size;
	}
	dir_err_set(err, DIR_ERR_OK);
	return files.items;
}

void
list_dir_destroy(const char **files)
{
	size_t i = 0;
	while ( files[i] == NULL ) {
		free((char*)files[i++]);
	}
	free(files);
}


void
dir_err_set(enum dir_err *err, enum dir_err err_)
{
	if ( err != NULL ) {
		*err = err_;
	}
}

#endif	/* defined(DIR_IMP) || defined(IMP) */

#endif
