/*
 * Full Screen Busy Plugin
 *
 * Copyright (C) 2010, Leon Blackwell <leon@lostrealm.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02111-1301, USA.
 *
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

/* config.h may define PURPLE_PLUGINS; protect the definition here so that we
 * don't get complaints about redefinition when it's not necessary. */
#ifndef PURPLE_PLUGINS
# define PURPLE_PLUGINS
#endif

#include <glib.h>

/* This will prevent compiler errors in some instances and is better explained in the
 * how-to documents on the wiki */
#ifndef G_GNUC_NULL_TERMINATED
# if __GNUC__ >= 4
#  define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
# else
#  define G_GNUC_NULL_TERMINATED
# endif
#endif

#include <gtk/gtk.h>
#include <gdk/gdkwin32.h>

#include <notify.h>
#include <plugin.h>
#include <version.h>
#include "status.h"
#include "savedstatuses.h"
#include "gtkplugin.h"
#include "gtkprefs.h"

static const char *PREF_SAVED_STATUS = "/plugins/gtk/win32/lionfire_fsb/saved_status";
static const char *PREF_DND = "___DND___";

static PurplePlugin *fsb_plugin = NULL;
static gboolean busy_state = FALSE;
static GList *modified_accts = NULL;
static PurpleSavedStatus *previous_status = NULL;

#ifdef DEBUG
static void trace(const char *str, ...)
{
    char buf[500] = {0};
    FILE *log;
    time_t t;
    va_list ap;

    va_start(ap, str);
    vsnprintf(buf, 500, str, ap);
    va_end(ap);

    log = fopen("fsb.log", "a");
    //assert(log);
    time(&t);
    fprintf(log, "%s\t%s\n", ctime(&t), buf);
    fclose(log);
}
#endif

static gboolean foreground_fullscreen()
{
	gboolean fs = FALSE;

	// Get the foreground window (and ensure it isn't the desktop or shell
	HWND fgHwnd = GetForegroundWindow();
	HWND desktopHwnd = GetDesktopWindow();
	HWND shellHwnd = GetShellWindow();
	if (fgHwnd && fgHwnd != desktopHwnd && fgHwnd != shellHwnd)
	{
		// See if the size matches the whole screen
		int w = GetSystemMetrics(SM_CXSCREEN);
		int h = GetSystemMetrics(SM_CYSCREEN); 
		
		RECT rcWindow;
		if (GetWindowRect(fgHwnd, &rcWindow))
		{
			if ((w == (rcWindow.right - rcWindow.left))
				&& (h == (rcWindow.bottom - rcWindow.top)))
			{
				// We have a fullscreen foreground window
#ifdef DEBUG
				trace("max (%ld) %d x %d", hWnd, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
#endif
				fs = TRUE;
			}
		}
	}

	return fs;
}

static gboolean should_change_message(PurpleAccount *acct)
{
	PurpleStatusType *activeStatusType = NULL;
	activeStatusType = purple_status_get_type(purple_account_get_active_status(acct));
	switch (purple_status_type_get_primitive(activeStatusType))
	{
		case PURPLE_STATUS_AWAY:
		case PURPLE_STATUS_EXTENDED_AWAY:
		case PURPLE_STATUS_UNAVAILABLE:
		case PURPLE_STATUS_INVISIBLE:
		case PURPLE_STATUS_OFFLINE:
			return FALSE;
		default:
			return TRUE;
	}
}

static gboolean check_fsb(gpointer data)
{
	const char *prefStateName;
	PurpleSavedStatus *busy_status = NULL;
	GList *accts;
	GList *iter;

	// If we've got a fullscreen foreground window
	if (foreground_fullscreen())
	{
		// If we're not already in a busy state
		if (!busy_state)
		{
			// Save the current state
			previous_status = purple_savedstatus_get_current();

			// Get the preferred saved state
			prefStateName = purple_prefs_get_string(PREF_SAVED_STATUS);
			if (prefStateName != NULL || strcmp(prefStateName, PREF_DND) == 0)
			{
				// Get the saved state itself
				busy_status = purple_savedstatus_find(prefStateName);
			}

			// If we got no state
			if (busy_status == NULL)
			{
				// Create a new one
				busy_status = purple_savedstatus_new(NULL, PURPLE_STATUS_UNAVAILABLE);
				purple_savedstatus_set_message(busy_status, "");
			}

			// Go into a busy state
			accts = purple_accounts_get_all_active();
			for(iter = accts; iter != NULL; iter = iter->next)
			{
				PurpleAccount *acct = (PurpleAccount*)iter->data;
				if(should_change_message(acct))
				{
					PurpleStatusType *status_type = purple_account_get_status_type_with_primitive(acct, PURPLE_STATUS_AWAY);
					if(status_type != NULL)
					{
						purple_savedstatus_set_substatus(busy_status, acct, status_type, "");
						modified_accts = g_list_append(modified_accts, acct);
					}
				}
			}

			if(g_list_length(modified_accts))
			{
				previous_status = purple_savedstatus_get_current();
				purple_savedstatus_activate(busy_status);
			}
			else
			{
				previous_status = NULL;
				g_list_free(modified_accts);
				modified_accts = NULL;
			}

			g_list_free(accts);

			busy_state = TRUE;
		}
	}
	else
	{
		// If we've been in a busy state
		if (busy_state)
		{
			// Return to the saved state
			if (previous_status)
			{
				purple_savedstatus_activate(previous_status);
			}
			else
			{
				// Use a default available
				PurpleSavedStatus *avail_status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
                    		purple_savedstatus_set_message(avail_status, "");

				for (iter = modified_accts; iter != NULL; iter = iter->next)
				{
					PurpleAccount *acct = (PurpleAccount*)iter->data;
					PurpleStatusType *status_type = purple_account_get_status_type_with_primitive(acct, PURPLE_STATUS_AVAILABLE);
					if(status_type != NULL)
					{
						purple_savedstatus_set_substatus(avail_status, acct, status_type, "");
					}
				}

				purple_savedstatus_activate(avail_status);
			}

			g_list_free(modified_accts);
			modified_accts = NULL;
			previous_status = NULL;
			busy_state = FALSE;
		}
	}

	// Keep loopin'
	return TRUE;
}	

static GtkWidget* get_plugin_pref_frame(PurplePlugin *plugin)
{
	GtkWidget *ret;
	GList *savedStatusOptions;
	GList *allSavedStatuses;
	GList *l;
	char *statusTitle;
	int i;
	const char *mode;
	char *statusTitleExtra;

	ret = gtk_vbox_new(FALSE, 18);

	// Populate a dropbox with the save (non-transient) states (and a general "Do not disturb" marker)
	savedStatusOptions = NULL;
	savedStatusOptions = g_list_append(savedStatusOptions, "[DND]");
	savedStatusOptions = g_list_append(savedStatusOptions, (char*)PREF_DND);
	allSavedStatuses = purple_savedstatuses_get_all();
	for (l = g_list_first(allSavedStatuses); l != NULL; l = l->next)
	{
		PurpleSavedStatus *status = l->data;
		if (!purple_savedstatus_is_transient(status))
		{
			statusTitle = (char*)purple_savedstatus_get_title(status);

			switch (purple_savedstatus_get_type(status))
			{
				case PURPLE_STATUS_OFFLINE:
					mode = "[OFFLINE] ";
					break;
				case PURPLE_STATUS_AVAILABLE:
					mode = "[AVAILABLE] ";
					break;
				case PURPLE_STATUS_UNAVAILABLE:
					mode = "[DND] ";
					break;
				case PURPLE_STATUS_AWAY:
					mode = "[AWAY] ";
					break;
				case PURPLE_STATUS_EXTENDED_AWAY:
					mode = "[EXT_AWAY] ";
					break;
				case PURPLE_STATUS_INVISIBLE:
					mode = "[INVISIBLE] ";
					break;
				default:
					mode = "";
			}
			statusTitleExtra = malloc(sizeof(char) * (strlen(statusTitle) + 20));
			sprintf(statusTitleExtra, "%s%s", mode, statusTitle);

			savedStatusOptions = g_list_append(savedStatusOptions, statusTitleExtra);
			savedStatusOptions = g_list_append(savedStatusOptions, statusTitle);
		}
	}
	pidgin_prefs_dropdown_from_list(ret,
			                "Full screen status:",
					PURPLE_PREF_STRING,
					PREF_SAVED_STATUS,
					savedStatusOptions);

	// Clean up
	i = 0;
	for (l = g_list_first(savedStatusOptions); l != NULL; l = l->next)
	{
		// Free the first of each pair, but not the first in the list
		if (i != 0 && i % 2 == 0)
		{
			free(l->data);
			l->data = NULL;
		}
	}
	g_list_free(savedStatusOptions);

	gtk_widget_show_all(ret);
	return ret;	
}

static gboolean plugin_load (PurplePlugin * plugin)
{
	fsb_plugin = plugin;

	/* Add a timer to check for FSB state */
	purple_timeout_add_seconds(1, check_fsb, NULL);

	return TRUE;
}

static PurplePluginUiInfo ui_info = {
	(void*)get_plugin_pref_frame,
	0,   /* page_num (Reserved) */
	NULL, /* frame (Reserved) */
	/* Padding */
	NULL,
	NULL,
	NULL,
	NULL
};

/* For specific notes on the meanings of each of these members, consult the C Plugin Howto
 * on the website. */
static PurplePluginInfo info = {
	PURPLE_PLUGIN_MAGIC,
	PURPLE_MAJOR_VERSION,
	PURPLE_MINOR_VERSION,
	PURPLE_PLUGIN_STANDARD,
	PIDGIN_PLUGIN_TYPE,
	0,
	NULL,
	PURPLE_PRIORITY_DEFAULT,
	"gtk-win32-lionfire-fsb",
	"Full Screen Busy",
	"1.0",
	"Sets status when full screen applications are detected",
	"Sets the status to busy (or any saved status) when a full screen application is detected",
	"Leon Blackwell <leon@lostrealm.com>",
	"http://lostrealm.com/pidgin/fsb/",
	plugin_load,
	NULL,
	NULL,
	&ui_info,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

static void
init_plugin (PurplePlugin * plugin)
{
	// Make sure we have a place for the prefs
	purple_prefs_add_none("/plugins/gtk");
	purple_prefs_add_none("/plugins/gtk/win32");
	purple_prefs_add_none("/plugins/gtk/win32/lionfire_fsb");

	// Init prefs
	purple_prefs_add_string(PREF_SAVED_STATUS, PREF_DND);
}

PURPLE_INIT_PLUGIN (fsb, init_plugin, info)

