#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <glib.h>

#include "../../include/disk.h"

#include "edv_utils.h"
#include "edv_mime_type.h"
#include "edv_context.h"
#include "edv_mime_type_get.h"


edv_mime_type_struct **EDVMimeTypeList(
	edv_context_struct *ctx,
	gint *total
);
edv_mime_type_struct *EDVMimeTypeMatch(
	edv_context_struct *ctx,
	const gchar *path,
	const struct stat *lstat_buf
);
edv_mime_type_struct *EDVMimeTypeMatchType(
	edv_context_struct *ctx,
	const gchar *type
);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	Gets the MIME Types list.
 *
 *	The ctx specifies the Endeavour 2 Context.
 *
 *	The total specifies the return value for the total number of MIME Types
 *	in the list.
 *
 *	The returned list must not be modified or deleted.
 */
edv_mime_type_struct **EDVMimeTypeList(
	edv_context_struct *ctx,
	gint *total
)
{
	if(total != NULL)
	    *total = 0;

	if(ctx == NULL)
	    return(NULL);

	if(total != NULL)
	    *total = ctx->total_mimetypes;

	return(ctx->mimetype);
}

/*
 *	Matches the MIME Type by path and object statistics.
 *
 *	The ctx specifies the Endeavour 2 Context.
 *
 *	The path specifies the full path or name of the object.
 *
 *	The lstat_buf specifies the local statistics of the object.
 *
 *	Returns the matched MIME Type or NULL on error, the returned
 *	MIME Type must not be modified or deleted.
 */
edv_mime_type_struct *EDVMimeTypeMatch(
	edv_context_struct *ctx,
	const gchar *path,
	const struct stat *lstat_buf
)
{
	mode_t st_mode;
	gint i, total;
	gboolean is_dir;
	edv_mime_type_struct *m, **list;

	if((ctx == NULL) || STRISEMPTY(path))
	    return(NULL);

	/* Get MIME Types list */
	list = ctx->mimetype;
	total = ctx->total_mimetypes;
	if(list == NULL)
	    return(NULL);

	/* Get object stats */
	st_mode = (lstat_buf != NULL) ? lstat_buf->st_mode : 0;
#ifdef S_ISDIR
	is_dir = S_ISDIR(st_mode) ? TRUE : FALSE;
#else
	is_dir = FALSE;
#endif

#ifdef S_ISLNK
	/* Is link? */
	if(S_ISLNK(st_mode))
	{
	    const gchar *link_type_str = EDV_MIME_TYPE_TYPE_INODE_LINK;

	    for(i = 0; i < total; i++)
	    {
		m = list[i];
		if(m == NULL)
		    continue;

		/* Only handle if MIME Type class is a systems object */
		if((m->mt_class == EDV_MIME_TYPE_CLASS_SYSTEM) &&
		   !STRISEMPTY(m->type)
		)
		{
		    if(!strcmp(m->type, link_type_str))
			return(m);
		}
	    }
	}
#endif

	/* Match for MIME Type classes unique, program, and format */
	if(TRUE)
	{
	    const gchar *value;

	    for(i = 0; i < total; i++)
	    {
		m = list[i];
		if(m == NULL)
		    continue;

		value = m->value;
		if(STRISEMPTY(value))
		    continue;

		/* Handle by MIME Type class */
		switch(m->mt_class)
		{
		  case EDV_MIME_TYPE_CLASS_SYSTEM:
		    break;
		  case EDV_MIME_TYPE_CLASS_UNIQUE:
		  case EDV_MIME_TYPE_CLASS_PROGRAM:
		    if((path != NULL) ? ISPATHABSOLUTE(path) : FALSE)
		    {
			if(!strcmp(value, path))
			    return(m);
		    }
		    break;
		  case EDV_MIME_TYPE_CLASS_FORMAT:
		    if(!is_dir)
		    {
			if(EDVIsExtension(path, value))
			    return(m);
		    }
		    break;
		}

		/* Do not stop iterating through MIME Types if got_match
		 * is TRUE, because there might be another MIME type
		 * that has a more specific icon further in the list
		 */

	    }
	}

	/* Match for system object */
	if(TRUE)
	{
	    const gchar *mime_type_str;

	    /* Get MIME Type string based on the object type */
#ifdef S_ISREG
	    if(S_ISREG(st_mode))
#else
	    if(FALSE)
#endif
	    {
		/* Check the file's permissions allow execution, in
		 * which case we use the file/executable MIME Type
		 * instead of file/regular
		 */
#if defined(S_IXUSR) && defined(S_IXGRP) && defined(S_IXOTH)
		if(st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
		    mime_type_str = EDV_MIME_TYPE_TYPE_INODE_EXECUTABLE;
		else
		    mime_type_str = EDV_MIME_TYPE_TYPE_INODE_FILE;
#else
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_FILE;
#endif
	    }
#ifdef S_ISDIR
	    else if(S_ISDIR(st_mode))
	    {
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_DIRECTORY;
	    }
#endif
#ifdef S_ISLNK
	    else if(S_ISLNK(st_mode))
	    {
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_LINK;
	    }
#endif
#ifdef S_ISBLK
	    else if(S_ISBLK(st_mode))
	    {
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_DEV_BLOCK;
	    }
#endif
#ifdef S_ISCHR
	    else if(S_ISCHR(st_mode))
	    {
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_DEV_CHARACTER;
	    }
#endif
#ifdef S_ISFIFO
	    else if(S_ISFIFO(st_mode))
	    {
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_FIFO;
	    }
#endif
#ifdef S_ISSOCK
	    else if(S_ISSOCK(st_mode))
	    {
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_SOCKET;
	    }
#endif
	    else
	    {
		/* All else assume file/regular */
		mime_type_str = EDV_MIME_TYPE_TYPE_INODE_UNKNOWN;
	    }

	    /* Iterate through system class MIME Types */
	    for(i = 0; i < total; i++)
	    {
		m = list[i];
		if(m == NULL)
		    continue;

		/* Only handle if MIME Type class is a systems object */
		if((m->mt_class == EDV_MIME_TYPE_CLASS_SYSTEM) &&
		   !STRISEMPTY(m->type)
		)
		{
		    if(!strcmp(m->type, mime_type_str))
			return(m);
		}
	    }
	}

	return(NULL);
}

/*
 *	Matches the MIME Type by type string.
 *
 *	The ctx specifies the Endeavour 2 Context.
 *
 *	The type specifies the type string.
 *
 *	Returns the matched MIME Type or NULL on error, the returned
 *	MIME Type must not be modified or deleted.
 */
edv_mime_type_struct *EDVMimeTypeMatchType(
	edv_context_struct *ctx,
	const gchar *type
)
{
	gint i, total;
	edv_mime_type_struct *m, **list;

	if((ctx == NULL) || STRISEMPTY(type))
	    return(NULL);

	/* Get MIME Types list */
	list = ctx->mimetype;
	total = ctx->total_mimetypes;
	if(list == NULL)
	    return(NULL);

	/* Iterate through system class MIME Types */
	for(i = 0; i < total; i++)
	{
	    m = list[i];
	    if(m == NULL)
		continue;

	    if(STRISEMPTY(m->type))
		continue;

	    if(!strcmp(m->type, type))
		return(m);
	}

	return(NULL);
}
