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

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

#include "edvutils.h"
#include "edvmimetypes.h"
#include "edvcontext.h"
#include "edvmimetypesget.h"


edv_mimetype_struct **EDVMimeTypeList(
	edv_context_struct *ctx, gint *total
);
edv_mimetype_struct *EDVMimeTypeMatch(
	edv_context_struct *ctx,
	const gchar *path, const struct stat *lstat_buf
);
edv_mimetype_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)


/*
 *	Returns a list of MIME Types.
 *
 *	The returned list must not be modified or deleted.
 */
edv_mimetype_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 object specified by path and lstat_buf with a
 *	MIME Type.
 *
 *	Returns the matched MIME Type or NULL on error, the returned
 *	MIME Type must not be modified or deleted.
 */
edv_mimetype_struct *EDVMimeTypeMatch(
	edv_context_struct *ctx,
	const gchar *path, const struct stat *lstat_buf
)
{
	gint i, total;
	gboolean is_dir;
	edv_mimetype_struct *m, **list;
	mode_t st_mode;


	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;
	is_dir = (S_ISDIR(st_mode)) ? TRUE : FALSE;

	/* Is link? */
	if(S_ISLNK(st_mode))
	{
	    const gchar *link_type_str = EDV_MIMETYPE_STR_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_MIMETYPE_CLASS_SYSTEM) &&
		   !STRISEMPTY(m->type)
		)
		{
		    if(!strcmp(m->type, link_type_str))
			return(m);
		}
	    }
	}

	/* 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_MIMETYPE_CLASS_SYSTEM:
		    break;
		  case EDV_MIMETYPE_CLASS_UNIQUE:
		  case EDV_MIMETYPE_CLASS_PROGRAM:
		    if((path != NULL) ? ISPATHABSOLUTE(path) : FALSE)
		    {
			if(!strcmp(value, path))
			    return(m);
		    }
		    break;
		  case EDV_MIMETYPE_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 */
	    if(S_ISREG(st_mode))
	    {
		/* Check the file's permissions allow execution, in
		 * which case we use the file/executable MIME Type
		 * instead of file/regular
		 */
		if(st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
		    mime_type_str = EDV_MIMETYPE_STR_FILE_EXECUTABLE;
		else
		    mime_type_str = EDV_MIMETYPE_STR_FILE;
	    }
	    else if(S_ISDIR(st_mode))
	    {
		mime_type_str = EDV_MIMETYPE_STR_DIRECTORY;
	    }
	    else if(S_ISLNK(st_mode))
	    {
		mime_type_str = EDV_MIMETYPE_STR_LINK;
	    }
	    else if(S_ISBLK(st_mode))
	    {
		mime_type_str = EDV_MIMETYPE_STR_DEVICE_BLOCK;
	    }
	    else if(S_ISCHR(st_mode))
	    {
		mime_type_str = EDV_MIMETYPE_STR_DEVICE_CHARACTER;
	    }
	    else if(S_ISFIFO(st_mode))
	    {
		mime_type_str = EDV_MIMETYPE_STR_FIFO;
	    }
	    else if(S_ISSOCK(st_mode))
	    {
		mime_type_str = EDV_MIMETYPE_STR_SOCKET;
	    }
	    else
	    {
		/* All else assume file/regular */
		mime_type_str = EDV_MIMETYPE_STR_FILE;
	    }

	    /* 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_MIMETYPE_CLASS_SYSTEM) &&
		   !STRISEMPTY(m->type)
		)
		{
		    if(!strcmp(m->type, mime_type_str))
			return(m);
		}
	    }
	}

	return(NULL);
}

/*
 *      Matches the MIME Type by the specified type string.
 *
 *      Returns the matched MIME Type or NULL on error, the returned
 *      MIME Type must not be modified or deleted.
 */
edv_mimetype_struct *EDVMimeTypeMatchType(
	edv_context_struct *ctx,
	const gchar *type
)
{
	gint i, total;
	edv_mimetype_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);
}
