/* Create new application tracker item */
static XfdashboardApplicationTrackerItem* _xfdashboard_application_tracker_item_new(GAppInfo *inAppInfo,
																					XfdashboardWindowTrackerWindow *inWindow)
{
	XfdashboardApplicationTrackerItem		*item;

	g_return_val_if_fail(G_IS_APP_INFO(inAppInfo), NULL);
	g_return_val_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_WINDOW(inWindow), NULL);

	/* Create new application item and set up */
	item=g_new0(XfdashboardApplicationTrackerItem, 1);
	item->pid=xfdashboard_window_tracker_window_get_pid(inWindow);
	item->appInfo=g_object_ref(inAppInfo);
	item->desktopID=g_strdup(g_app_info_get_id(inAppInfo));
	item->windows=g_list_prepend(item->windows, inWindow);

	/* Return newly created application tracker item */
	return(item);
}
/* Get desktop ID from process' environment which owns window.
 * Callee is responsible to free result with g_object_unref().
 */
static GAppInfo* _xfdashboard_application_tracker_get_desktop_id_from_environment(XfdashboardApplicationTracker *self,
																					XfdashboardWindowTrackerWindow *inWindow)
{
	XfdashboardApplicationTrackerPrivate	*priv;
	GAppInfo								*foundAppInfo;
	gint									windowPID;
	gchar									*procEnvFile;
	gchar									*envContent;
	gsize									envLength;
	GError									*error;
	gchar									*iter;
	const gchar								*gioLaunchedPID;
	const gchar								*gioLaunchedDesktopFile;

	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION_TRACKER(self), NULL);
	g_return_val_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_WINDOW(inWindow), NULL);

	priv=self->priv;
	foundAppInfo=NULL;
	error=NULL;

	/* Get process ID running this window */
	windowPID=xfdashboard_window_tracker_window_get_pid(inWindow);
	if(windowPID<=0)
	{
		g_debug("Could not get PID for window '%s' of a running application to parse environment variables",
				xfdashboard_window_tracker_window_get_title(inWindow));

		return(NULL);
	}

	/* Open enviroment variables of process.
	 * This is the initial set of environment variables set when process was spawned.
	 * But that is ok because the environment variables we lookup are set
	 * at launch time and do not change.
	 */
	envContent=NULL;
	envLength=0;

	procEnvFile=g_strdup_printf("/proc/%d/environ", windowPID);
	if(!g_file_get_contents(procEnvFile, &envContent, &envLength, &error))
	{
		g_debug("Could read in enviroment varibles for PID %d of window '%s' at %s: %s",
					windowPID,
					xfdashboard_window_tracker_window_get_title(inWindow),
					procEnvFile,
					error ? error->message : _("Unknown error"));

		/* Release allocated resources */
		if(error) g_error_free(error);
		if(procEnvFile) g_free(procEnvFile);
		if(envContent) g_free(envContent);

		/* Return NULL result */
		return(NULL);
	}

	g_debug("Enviroment set at %s is %lu bytes long for window '%s'",
				procEnvFile,
				envLength,
				xfdashboard_window_tracker_window_get_title(inWindow));

	/* Iterate through enviroment variables and lookup GIO_LAUNCHED_DESKTOP_FILE
	 * and GIO_LAUNCHED_DESKTOP_FILE_PID.
	 */
	gioLaunchedPID=NULL;
	gioLaunchedDesktopFile=NULL;

	iter=envContent;
	while(envLength>0)
	{
		gsize								len;

		/* Skip NULL-termination */
		if(!*iter)
		{
			envLength--;
			iter++;
			continue;
		}

		/* Check current iterated enviroment variable matches a requested one */
		if(g_str_has_prefix(iter, "GIO_LAUNCHED_DESKTOP_FILE="))
		{
			/* Each enviroment variable should be listed only once
			 * otherwise is an error.
			 */
			if(gioLaunchedDesktopFile)
			{
				g_debug("Could parse in enviroment varibles for PID %d of window '%s' at %s because GIO_LAUNCHED_DESKTOP_FILE exists more than once",
							windowPID,
							xfdashboard_window_tracker_window_get_title(inWindow),
							procEnvFile);

				/* Release allocated resources */
				if(foundAppInfo) g_object_unref(foundAppInfo);
				if(procEnvFile) g_free(procEnvFile);
				if(envContent) g_free(envContent);

				/* Return NULL result */
				return(NULL);
			}

			/* Remember value of environment variable */
			gioLaunchedDesktopFile=iter;
		}
			else if(g_str_has_prefix(iter, "GIO_LAUNCHED_DESKTOP_FILE_PID="))
			{
				/* Each enviroment variable should be listed only once
				 * otherwise is an error.
				 */
				if(gioLaunchedPID)
				{
					g_debug("Could parse in enviroment varibles for PID %d of window '%s' at %s because GIO_LAUNCHED_DESKTOP_FILE_PID exists more than once",
								windowPID,
								xfdashboard_window_tracker_window_get_title(inWindow),
								procEnvFile);

					/* Release allocated resources */
					if(foundAppInfo) g_object_unref(foundAppInfo);
					if(procEnvFile) g_free(procEnvFile);
					if(envContent) g_free(envContent);

					/* Return NULL result */
					return(NULL);
				}

				/* Remember value of environment variable */
				gioLaunchedPID=iter;
			}

		/* If all requested environment variable has been found stop iterating */
		if(gioLaunchedPID && gioLaunchedDesktopFile) break;

		/* Continue with next environment variable */
		len=strlen(iter);
		iter+=len;
		envLength-=len;
	}

	/* If all requested environment variable has been found then check if
	 * GIO_LAUNCHED_DESKTOP_FILE_PID matches window owner's process ID.
	 */
	if(gioLaunchedPID && gioLaunchedDesktopFile)
	{
		/* Move pointer of environment variables to value */
		while(*gioLaunchedPID && *gioLaunchedPID!='=') gioLaunchedPID++;
		while(*gioLaunchedDesktopFile && *gioLaunchedDesktopFile!='=') gioLaunchedDesktopFile++;

		/* Check if pointers points to value assignment character */
		if(*gioLaunchedPID=='=' &&
			*gioLaunchedDesktopFile=='=')
		{
			gint							checkPID;

			/* Move pointer one byte further where value begins really */
			gioLaunchedPID++;
			gioLaunchedDesktopFile++;

			/* Check if PID of enviroment variable matches window owner's
			 * process ID.
			 */
			checkPID=atoi(gioLaunchedPID);
			if(checkPID==windowPID)
			{
				/* Lookup application from full path */
				foundAppInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDatabase, gioLaunchedDesktopFile);
				if(!foundAppInfo)
				{
					/* Lookup application from basename of path */
					gioLaunchedDesktopFile=g_strrstr(gioLaunchedDesktopFile, G_DIR_SEPARATOR_S);
					if(gioLaunchedDesktopFile)
					{
						gioLaunchedDesktopFile++;
						foundAppInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDatabase, gioLaunchedDesktopFile);
					}
				}
			}
				else
				{
					g_debug("PID %d of environment variable does not match window PID %d for '%s'",
								checkPID,
								windowPID,
								xfdashboard_window_tracker_window_get_title(inWindow));
				}
		}
	}

	/* Release allocated resources */
	if(procEnvFile) g_free(procEnvFile);
	if(envContent) g_free(envContent);

	/* Return found application info */
	g_debug("Resolved enviroment variables of window '%s' to desktop ID '%s'",
				xfdashboard_window_tracker_window_get_title(inWindow),
				foundAppInfo ? g_app_info_get_id(foundAppInfo) : "<nil>");

	return(foundAppInfo);
}
/* Get desktop ID from process' environment which owns window.
 * Callee is responsible to free result with g_object_unref().
 */
static GAppInfo* _xfdashboard_application_tracker_get_desktop_id_from_environment(XfdashboardApplicationTracker *self,
																					XfdashboardWindowTrackerWindow *inWindow)
{
	XfdashboardApplicationTrackerPrivate	*priv;
	GAppInfo								*foundAppInfo;
	gint									windowPID;
	GHashTable								*environments;
	gchar									*value;
	gint									checkPID;

	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION_TRACKER(self), NULL);
	g_return_val_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_WINDOW(inWindow), NULL);

	priv=self->priv;
	foundAppInfo=NULL;

	/* Get process ID running this window */
	windowPID=xfdashboard_window_tracker_window_get_pid(inWindow);
	if(windowPID<=0)
	{
		g_debug("Could not get PID for window '%s' of a running application to parse environment variables",
				xfdashboard_window_tracker_window_get_title(inWindow));

		/* Return NULL result */
		return(NULL);
	}

	/* Get hash-table with environment variables found for window's PID */
	environments=_xfdashboard_application_tracker_get_environment_from_pid(windowPID);
	if(!environments)
	{
		g_debug("Could not get environments for PID %d of windows '%s'",
				windowPID,
				xfdashboard_window_tracker_window_get_title(inWindow));

		/* Return NULL result */
		return(NULL);
	}

	/* Check that environment variable GIO_LAUNCHED_DESKTOP_FILE_PID exists.
	 * Also check that the PID in value matches the requested window's PID
	 * as the process may inherit the environments of its parent process
	 * but then this one is not the initial process for this application.
	 */
	if(!g_hash_table_lookup_extended(environments, "GIO_LAUNCHED_DESKTOP_FILE_PID", NULL, (gpointer)&value))
	{
		g_debug("Missing 'GIO_LAUNCHED_DESKTOP_FILE_PID' in environment variables for PID %d of windows '%s'",
					windowPID,
					xfdashboard_window_tracker_window_get_title(inWindow));

		/* Release allocated resources */
		if(environments) g_hash_table_destroy(environments);

		/* Return NULL result */
		return(NULL);
	}

	checkPID=atoi(value);
	if(checkPID!=windowPID)
	{
		g_debug("PID %d of environment variables does not match requested window PID %d for '%s'",
					checkPID,
					windowPID,
					xfdashboard_window_tracker_window_get_title(inWindow));

		/* Release allocated resources */
		if(environments) g_hash_table_destroy(environments);

		/* Return NULL result */
		return(NULL);
	}

	/* Check that environment variable GIO_LAUNCHED_DESKTOP_FILE exists and
	 * lookup application from full path as set in environment's value.
	 */
	if(!g_hash_table_lookup_extended(environments, "GIO_LAUNCHED_DESKTOP_FILE", NULL, (gpointer)&value))
	{
		g_debug("Missing 'GIO_LAUNCHED_DESKTOP_FILE' in environment variables for PID %d of windows '%s'",
					windowPID,
					xfdashboard_window_tracker_window_get_title(inWindow));

		/* Release allocated resources */
		if(environments) g_hash_table_destroy(environments);

		/* Return NULL result */
		return(NULL);
	}

	foundAppInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDatabase, value);
	if(!foundAppInfo)
	{
		/* Lookup application from basename of path */
		value=g_strrstr(value, G_DIR_SEPARATOR_S);
		if(value)
		{
			value++;
			foundAppInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDatabase, value);
		}
	}

	/* Release allocated resources */
	if(environments) g_hash_table_destroy(environments);

	/* Return found application info which may be NULL if not found in
	 * application database.
	 */
	g_debug("Resolved enviroment variables of window '%s' to desktop ID '%s'",
				xfdashboard_window_tracker_window_get_title(inWindow),
				foundAppInfo ? g_app_info_get_id(foundAppInfo) : "<nil>");

	return(foundAppInfo);
}