#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__SOLARIS__)
# include <sys/mnttab.h>
# include <sys/vfstab.h>
#else
# include <mntent.h>
#endif
#include <glib.h>

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

#include "edvtypes.h"
#include "edvdevices.h"
#include "edvdevicesfio.h"

#include "config.h"


void EDVDevicesListUpdateMountStatesFromSystem(
	edv_device_struct **list, gint total
);

edv_device_struct **EDVDevicesListLoadFromFile(
	const gchar *filename, gint *total
);
void EDVDeviceListSaveToFile(
	const gchar *filename,
	edv_device_struct **list, gint total
);


#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)


/*
 *	Updates the mount states of all Devices in the list to
 *	reflect current mount states of the devices from the system.
 */
void EDVDevicesListUpdateMountStatesFromSystem(
	edv_device_struct **list, gint total
)
{
	FILE *fp;
	gint dev_num = -1;
	edv_device_struct *dev_ptr = NULL;
#if defined(__SOLARIS__)
	struct mnttab *mt_ptr = NULL;
	int mtback;
#else
	struct mntent *mt_ptr;
#endif

	if((list == NULL) || (total <= 0))
	    return;

	/* Reset mount states on all Devices */
	for(dev_num = 0; dev_num < total; dev_num++)
	{
	    dev_ptr = list[dev_num];
	    if(dev_ptr == NULL)
		continue;

	    dev_ptr->is_mounted = FALSE;
	}

	/* Open the system's mounted devices list file */
#if defined(__SOLARIS__)
	fp = FOpen("/etc/mnttab", "rb");
#else
	fp = setmntent("/etc/mtab", "rb");
#endif
	if(fp == NULL)
	    return;

	/* Begin reading the system's mounted devices list file */
#if defined(__SOLARIS__)
	mt_ptr = (struct mnttab *)g_malloc(sizeof(struct mnttab));
	mtback = getmntent(fp, mt_ptr);
	while(mtback != 0)
#else
	mt_ptr = getmntent(fp);
	while(mt_ptr != NULL)
#endif
	{
	    /* Check if the mounted device path matches a Device in
	     * the specified list
	     */
	    dev_ptr = EDVDeviceListMatchDevicePath(
		list, total,
#if defined(__SOLARIS__)
		&dev_num, mt_ptr->mnt_special
#else
		&dev_num, mt_ptr->mnt_fsname
#endif
	    );
	    if(dev_ptr != NULL)
	    {
		/* Entry from system's list of mounted devices matches
		 * one of the Devices in the list so mark that Device as
		 * mounted
		 */
		dev_ptr->is_mounted = TRUE;
	    }

	    /* Read next mount entry */
#if defined(__SOLARIS__)
	    mtback = getmntent(fp, mt_ptr);
#else
	    mt_ptr = getmntent(fp);
#endif
	}


	/* Close system mounted devices list file */
#if defined(__SOLARIS__)
	FClose(fp);
#else
	endmntent(fp);
#endif
}


/*
 *	Loads all Devices from the specified file.
 */
edv_device_struct **EDVDevicesListLoadFromFile(
	const gchar *filename, gint *total
)
{
	gchar *parm;
	FILE *fp;
	gint dev_num = -1;
	edv_device_struct **list = NULL, *dev_ptr = NULL;


	if(total != NULL)
	    *total = 0;

	if((total == NULL) || STRISEMPTY(filename))
	    return(NULL);

	/* Open devices file */
	fp = FOpen(filename, "rb");
	if(fp == NULL)
	    return(NULL);

	/* Begin file */
	parm = NULL;
	while(TRUE)
	{
	    /* Read next parameter */
	    parm = FSeekNextParm(
		fp, parm,
		EDV_CFG_COMMENT_CHAR,
		EDV_CFG_DELIMINATOR_CHAR
	    );
	    if(parm == NULL)
		break;

	    /* Begin handling by parameter */

	    /* BeginDevice */
	    if(!g_strcasecmp(parm, "BeginDevice"))
	    {
		gchar *device_path = FGetString(fp);

		/* Check if an existing Device already exists in
		 * the list that matches the device_path
		 */
		dev_ptr = EDVDeviceListMatchDevicePath(
		    list, *total,
		    &dev_num, device_path
		);
		if(dev_ptr == NULL)
		{
		    /* No existing Device found that matches the
		     * device_path so create a new Device
		     */
		    dev_num = *total;
		    *total = dev_num + 1;
		    list = (edv_device_struct **)g_realloc(
			list, (*total) * sizeof(edv_device_struct *)
		    );
		    if(list == NULL)
		    {
			*total = 0;
			dev_num = -1;
			dev_ptr = NULL;
		    }
		    else
		    {
			list[dev_num] = dev_ptr = EDVDeviceNew(
			    EDV_FS_TYPE_EMPTY, NULL, device_path, NULL
			);
			if(dev_ptr == NULL)
			{
			    dev_num = -1;
			}
		    }
		}
	    }

	    /* FSType */
	    else if(!g_strcasecmp(parm, "FSType") ||
		    !g_strcasecmp(parm, "FileSystemType")
	    )
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev_ptr != NULL)
		    dev_ptr->fs_type = v[0];
	    }
	    /* NoUnmount */
	    else if(!g_strcasecmp(parm, "NoUnmount"))
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev_ptr != NULL)
		    dev_ptr->no_unmount = (gboolean)v[0];
	    }
	    /* ReadOnly */
	    else if(!g_strcasecmp(parm, "ReadOnly"))
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev_ptr != NULL)
		    dev_ptr->read_only = (gboolean)v[0];
	    }
	    /* Unlisted */
	    else if(!g_strcasecmp(parm, "Unlisted"))
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev_ptr != NULL)
		    dev_ptr->unlisted = (gboolean)v[0];
	    }
	    /* Name */
	    else if(!g_strcasecmp(parm, "Name"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->name);
		    dev_ptr->name = STRDUP(v);
		}
		g_free(v);
	    }
	    /* MountPath */
	    else if(!g_strcasecmp(parm, "MountPath"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->mount_path);
		    dev_ptr->mount_path = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandMount */
	    else if(!g_strcasecmp(parm, "CommandMount"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->command_mount);
		    dev_ptr->command_mount = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandUnmount */
	    else if(!g_strcasecmp(parm, "CommandUnmount"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->command_unmount);
		    dev_ptr->command_unmount = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandEject */
	    else if(!g_strcasecmp(parm, "CommandEject"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->command_eject);
		    dev_ptr->command_eject = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandCheck */
	    else if(!g_strcasecmp(parm, "CommandCheck"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->command_check);
		    dev_ptr->command_check = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandTools */
	    else if(!g_strcasecmp(parm, "CommandTools"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->command_tools);
		    dev_ptr->command_tools = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandFormat */
	    else if(!g_strcasecmp(parm, "CommandFormat"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    g_free(dev_ptr->command_format);
		    dev_ptr->command_format = STRDUP(v);
		}
		g_free(v);
	    }

	    /* IconSmallStandard */
	    else if(!g_strcasecmp(parm, "IconSmallStandard"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->small_icon_file[
			EDV_DEVICE_ICON_STATE_STANDARD
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconSmallSelected */
	    else if(!g_strcasecmp(parm, "IconSmallSelected"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->small_icon_file[
			EDV_DEVICE_ICON_STATE_SELECTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconSmallUnmounted */
	    else if(!g_strcasecmp(parm, "IconSmallUnmounted"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->small_icon_file[
			EDV_DEVICE_ICON_STATE_UNMOUNTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }

	    /* IconMediumStandard */
	    else if(!g_strcasecmp(parm, "IconMediumStandard"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->medium_icon_file[
			EDV_DEVICE_ICON_STATE_STANDARD
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconMediumSelected */
	    else if(!g_strcasecmp(parm, "IconMediumSelected"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->medium_icon_file[
		        EDV_DEVICE_ICON_STATE_SELECTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconMediumUnmounted */
	    else if(!g_strcasecmp(parm, "IconMediumUnmounted"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->medium_icon_file[
			EDV_DEVICE_ICON_STATE_UNMOUNTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }

	    /* IconLargeStandard */
	    else if(!g_strcasecmp(parm, "IconLargeStandard"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->large_icon_file[
		        EDV_DEVICE_ICON_STATE_STANDARD
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconLargeSelected */
	    else if(!g_strcasecmp(parm, "IconLargeSelected"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->large_icon_file[
			EDV_DEVICE_ICON_STATE_SELECTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconLargeUnmounted */
	    else if(!g_strcasecmp(parm, "IconLargeUnmounted"))
	    {
		gchar *v = FGetString(fp);
		if(dev_ptr != NULL)
		{
		    gchar **t = &dev_ptr->large_icon_file[
			EDV_DEVICE_ICON_STATE_UNMOUNTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }

	    /* LastMountTime */
	    else if(!g_strcasecmp(parm, "LastMountTime"))
	    {
		glong v[1];
		FGetValuesL(fp, v, 1);
		if(dev_ptr != NULL)
		    dev_ptr->last_mount_time = (gulong)v[0];
	    }
	    /* LastCheckTime */
	    else if(!g_strcasecmp(parm, "LastCheckTime"))
	    {
		glong v[1];
		FGetValuesL(fp, v, 1);
		if(dev_ptr != NULL)
		    dev_ptr->last_check_time = (gulong)v[0];
	    }

	    /* End of a device block? */
	    else if(!g_strcasecmp(parm, "EndDevice"))
	    {
		FSeekNextLine(fp);

		/* Reset contexts */
		dev_num = -1;
		dev_ptr = NULL;
	    }
	    /* All else unsupported parameter or wrong context */
	    else
	    {
		FSeekNextLine(fp);
	    }
	}

	/* Delete parameter */
	g_free(parm);

	/* Close devices file */
	FClose(fp);

	return(list);
}

/*
 *	Saves the list of devices to the specified file.
 */
void EDVDeviceListSaveToFile(
	const gchar *filename,
	edv_device_struct **list, gint total
)
{
	gint dev_num;
	const edv_device_struct *dev_ptr;
	gchar *parent_path;
	FILE *fp;
	const gchar *path;


	if((list == NULL) || STRISEMPTY(filename))
	    return;

	/* Get parent directory and create it as needed */
	parent_path = g_dirname(filename);
	if(parent_path != NULL)
	{
	    rmkdir(parent_path, S_IRUSR | S_IWUSR | S_IXUSR);
	    g_free(parent_path);
	}

	/* Open devices file for writing */
	fp = FOpen(filename, "wb");
	if(fp == NULL)
	    return;

	/* Iterate through all device structures */
	for(dev_num = 0; dev_num < total; dev_num++)
	{
	    dev_ptr = list[dev_num];
	    if(dev_ptr == NULL)
		continue;

	    /* BeginDevice */
	    fprintf(
		fp,
		"BeginDevice = %s\n",
		dev_ptr->device_path
	    );

	    /* FSType */
	    fprintf(
		fp,
		"\tFSType = %i\n",
		dev_ptr->fs_type
	    );
	    /* NoUnmount */
	    fprintf(
		fp,
		"\tNoUnmount = %i\n",
		dev_ptr->no_unmount
	    );
	    /* ReadOnly */
	    fprintf(
		fp,
		"\tReadOnly = %i\n",
		dev_ptr->read_only
	    );
	    /* Unlisted */
	    fprintf(
		fp,
		"\tUnlisted = %i\n",
		dev_ptr->unlisted
	    );
	    /* Name */
	    if(!STRISEMPTY(dev_ptr->name))
		fprintf(
		    fp,
		    "\tName = %s\n",
		    dev_ptr->name
		);
	    /* MountPath */
	    if(!STRISEMPTY(dev_ptr->mount_path))
		fprintf(
		    fp,
		    "\tMountPath = %s\n",
		    dev_ptr->mount_path
		);

	    /* CommandMount */
	    if(!STRISEMPTY(dev_ptr->command_mount))
		fprintf(
		    fp,
		    "\tCommandMount = %s\n",
		    dev_ptr->command_mount
		);
	    /* CommandUnmount */
	    if(!STRISEMPTY(dev_ptr->command_unmount))
		fprintf(
		    fp,
		    "\tCommandUnmount = %s\n",
		    dev_ptr->command_unmount
		);
	    /* CommandEject */
	    if(!STRISEMPTY(dev_ptr->command_eject))
		fprintf(
		    fp,
		    "\tCommandEject = %s\n",
		    dev_ptr->command_eject
		);
	    /* CommandCheck */
	    if(!STRISEMPTY(dev_ptr->command_check))
		fprintf(
		    fp,
		    "\tCommandCheck = %s\n",
		    dev_ptr->command_check
		);
	    /* CommandTools */
	    if(!STRISEMPTY(dev_ptr->command_tools))
		fprintf(
		    fp,
		    "\tCommandTools = %s\n",
		    dev_ptr->command_tools
		);
	    /* CommandFormat */
	    if(!STRISEMPTY(dev_ptr->command_format))
		fprintf(
		    fp,
		    "\tCommandFormat = %s\n",
		    dev_ptr->command_format
		);

	    /* IconSmallStandard */
	    path = dev_ptr->small_icon_file[EDV_DEVICE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallStandard = %s\n",
		    path
		);
	    /* IconSmallSelected */
	    path = dev_ptr->small_icon_file[EDV_DEVICE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallSelected = %s\n",
		    path
		);
	    /* IconSmallUnmounted */
	    path = dev_ptr->small_icon_file[EDV_DEVICE_ICON_STATE_UNMOUNTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallUnmounted = %s\n",
		    path
		);

	    /* IconMediumStandard */
	    path = dev_ptr->medium_icon_file[EDV_DEVICE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumStandard = %s\n",
		    path
		);
	    /* IconMediumSelected */
	    path = dev_ptr->medium_icon_file[EDV_DEVICE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumSelected = %s\n",
		    path
		);
	    /* IconMediumUnmounted */
	    path = dev_ptr->medium_icon_file[EDV_DEVICE_ICON_STATE_UNMOUNTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumUnmounted = %s\n",
		    path
		);

	    /* IconLargeStandard */
	    path = dev_ptr->large_icon_file[EDV_DEVICE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeStandard = %s\n",
		    path
		);
	    /* IconLargeSelected */
	    path = dev_ptr->large_icon_file[EDV_DEVICE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeSelected = %s\n",
		    path
		);
	    /* IconLargeUnmounted */
	    path = dev_ptr->large_icon_file[EDV_DEVICE_ICON_STATE_UNMOUNTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeUnmounted = %s\n",
		    path
		);

	    /* LastMountTime */
	    if(dev_ptr->last_mount_time > 0l)
		fprintf(
		    fp,
		    "\tLastMountTime = %ld\n",
		    dev_ptr->last_mount_time
		);
	    /* LastCheckTime */
	    if(dev_ptr->last_check_time > 0l)
		fprintf(
		    fp,
		    "\tLastCheckTime = %ld\n",
		    dev_ptr->last_check_time
		);

	    /* EndDevice */
	    fprintf(
		fp,
		"EndDevice\n"
	    );
	}

	/* Close devices file */
	FClose(fp);
}
