/* $Id$ */
/* Copyright (c) 2011-2022 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Locker */
/* Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */



#include <System.h>
#ifdef __NetBSD__
# include <sys/param.h>
# include <sys/sysctl.h>
# include <errno.h>
#else
# include <fcntl.h>
# include <unistd.h>
#endif
#include <string.h>
#include "Locker.h"


/* Slider */
/* private */
/* types */
typedef struct _LockerAuth
{
	LockerAuthHelper * helper;
	guint source;
	gboolean locked;

	/* widgets */
	GtkWidget * widget;
	GtkWidget * icon1;
	GtkWidget * scale;
	GtkWidget * icon2;
} Slider;

typedef struct _SliderTheme
{
	char const * name;
	char const * icon1;
	char const * icon2;
} SliderTheme;


/* constants */
static const SliderTheme _slider_themes[] =
{
	{ "changes", "changes-prevent", "changes-allow" },
	{ "dialog", "dialog-error", "dialog-information" },
	{ "emblem", "emblem-important", "emblem-default" },
	{ "filesystem", "emblem-readonly", "emblem-shared" },
	{ "security", "security-high", "security-low" },
	{ "smiley", "face-sad", "face-smile" },
	{ "user", "user-idle", "user-available" },
	{ NULL, NULL, NULL }
};
#define SLIDER_THEME_DEFAULT 0


/* prototypes */
/* plug-in */
static Slider * _slider_init(LockerAuthHelper * helper);
static void _slider_destroy(Slider * slider);
static GtkWidget * _slider_get_widget(Slider * slider);
static int _slider_action(Slider * slider, LockerAction action);

/* useful */
static int _slider_load(Slider * slider);

/* callbacks */
static void _slider_on_scale_value_changed(gpointer data);
static gboolean _slider_on_scale_value_changed_timeout(gpointer data);
static gboolean _slider_on_timeout(gpointer data);


/* public */
/* variables */
/* plug-in */
LockerAuthDefinition plugin =
{
	"Slider",
	NULL,
	NULL,
	_slider_init,
	_slider_destroy,
	_slider_get_widget,
	_slider_action
};


/* private */
/* functions */
/* slider_init */
static Slider * _slider_init(LockerAuthHelper * helper)
{
	Slider * slider;
	GtkWidget * vbox;
	GtkWidget * hbox;

	if((slider = object_new(sizeof(*slider))) == NULL)
		return NULL;
	slider->helper = helper;
	slider->source = 0;
	slider->locked = FALSE;
#if GTK_CHECK_VERSION(3, 0, 0)
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
#else
	vbox = gtk_vbox_new(FALSE, 4);
#endif
	slider->widget = vbox;
#if GTK_CHECK_VERSION(3, 0, 0)
	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
#else
	hbox = gtk_hbox_new(FALSE, 4);
#endif
	slider->icon1 = NULL;
	slider->scale = NULL;
	slider->icon2 = NULL;
	_slider_load(slider);
	/* left image */
#if GTK_CHECK_VERSION(3, 0, 0)
	g_object_set(slider->icon1, "halign", GTK_ALIGN_END,
			"margin-bottom", 96, "margin-top", 96, NULL);
#else
	gtk_misc_set_alignment(GTK_MISC(slider->icon1), 1.0, 0.5);
	gtk_misc_set_padding(GTK_MISC(slider->icon1), 0, 96);
#endif
	gtk_box_pack_start(GTK_BOX(hbox), slider->icon1, TRUE, TRUE, 0);
	/* scale */
#if GTK_CHECK_VERSION(3, 0, 0)
	slider->scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,
			0.0, 100.0, 1.0);
#else
	slider->scale = gtk_hscale_new_with_range(0.0, 100.0, 1.0);
#endif
	gtk_range_set_value(GTK_RANGE(slider->scale), 0.0);
	gtk_scale_set_draw_value(GTK_SCALE(slider->scale), FALSE);
	gtk_widget_set_size_request(slider->scale, 240, -1);
	g_signal_connect_swapped(slider->scale, "value-changed", G_CALLBACK(
				_slider_on_scale_value_changed), slider);
	gtk_box_pack_start(GTK_BOX(hbox), slider->scale, FALSE, TRUE, 0);
	/* right image */
#if GTK_CHECK_VERSION(3, 0, 0)
	g_object_set(slider->icon2, "halign", GTK_ALIGN_START,
			"margin-bottom", 96, "margin-top", 96, NULL);
#else
	gtk_misc_set_alignment(GTK_MISC(slider->icon2), 0.0, 0.5);
	gtk_misc_set_padding(GTK_MISC(slider->icon2), 0, 96);
#endif
	gtk_box_pack_start(GTK_BOX(hbox), slider->icon2, TRUE, TRUE, 0);
	gtk_widget_show_all(hbox);
	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
	return slider;
}


/* slider_destroy */
static void _slider_destroy(Slider * slider)
{
	gtk_widget_destroy(slider->widget);
	if(slider->source != 0)
		g_source_remove(slider->source);
	object_delete(slider);
}


/* slider_get_widget */
static GtkWidget * _slider_get_widget(Slider * slider)
{
	return slider->widget;
}


/* slider_action */
static int _slider_action(Slider * slider, LockerAction action)
{
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%u)\n", __func__, action);
#endif
	switch(action)
	{
		case LOCKER_ACTION_DEACTIVATE:
			if(slider->source != 0)
				g_source_remove(slider->source);
			slider->source = 0;
			if(slider->locked)
			{
				gtk_range_set_value(GTK_RANGE(slider->scale),
						0.0);
				gtk_widget_grab_focus(slider->scale);
				gtk_widget_show(slider->widget);
				slider->source = g_timeout_add(3000,
						_slider_on_timeout, slider);
			}
			break;
		case LOCKER_ACTION_LOCK:
			gtk_widget_hide(slider->widget);
			if(slider->source != 0)
				g_source_remove(slider->source);
			slider->source = 0;
			slider->locked = TRUE;
			break;
		case LOCKER_ACTION_UNLOCK:
			slider->locked = FALSE;
			/* fallthrough */
		case LOCKER_ACTION_ACTIVATE:
		case LOCKER_ACTION_CYCLE:
		case LOCKER_ACTION_START:
			gtk_widget_hide(slider->widget);
			if(slider->source != 0)
				g_source_remove(slider->source);
			slider->source = 0;
			break;
		default:
			break;
	}
	return 0;
}


/* useful */
/* slider_load */
static int _slider_load(Slider * slider)
{
	LockerAuthHelper * helper = slider->helper;
	size_t i = SLIDER_THEME_DEFAULT;
	String const * p;

	/* load the theme configured */
	if((p = helper->config_get(helper->locker, "slider", "theme")) != NULL)
		for(i = 0; _slider_themes[i].name != NULL; i++)
			if(strcmp(_slider_themes[i].name, p) == 0)
				break;
	/* load the first icon */
	if((p = _slider_themes[i].icon1) == NULL
			&& (p = helper->config_get(helper->locker, "slider",
					"icon1")) == NULL)
		p = _slider_themes[SLIDER_THEME_DEFAULT].icon1;
	if(slider->icon1 == NULL)
		slider->icon1 = gtk_image_new_from_icon_name(p,
				GTK_ICON_SIZE_LARGE_TOOLBAR);
	else
		gtk_image_set_from_icon_name(GTK_IMAGE(slider->icon1), p,
				GTK_ICON_SIZE_LARGE_TOOLBAR);
	/* load the second icon */
	if((p = _slider_themes[i].icon2) == NULL
			&& (p = helper->config_get(helper->locker, "slider",
					"icon2")) == NULL)
		p = _slider_themes[SLIDER_THEME_DEFAULT].icon2;
	if(slider->icon2 == NULL)
		slider->icon2 = gtk_image_new_from_icon_name(p,
				GTK_ICON_SIZE_LARGE_TOOLBAR);
	else
		gtk_image_set_from_icon_name(GTK_IMAGE(slider->icon2), p,
				GTK_ICON_SIZE_LARGE_TOOLBAR);
	return 0;
}


/* callbacks */
/* slider_on_scale_value_changed */
static void _slider_on_scale_value_changed(gpointer data)
{
	Slider * slider = data;
	LockerAuthHelper * helper = slider->helper;
	gdouble value;

	if(slider->source != 0)
		g_source_remove(slider->source);
	slider->source = 0;
	value = gtk_range_get_value(GTK_RANGE(slider->scale));
	if(value >= 100.0)
		helper->action(helper->locker, LOCKER_ACTION_UNLOCK);
	else if(value > 0.0)
		slider->source = g_timeout_add(1000,
				_slider_on_scale_value_changed_timeout, slider);
}


/* slider_on_scale_value_changed_timeout */
static gboolean _slider_on_scale_value_changed_timeout(gpointer data)
{
	Slider * slider = data;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
	gtk_range_set_value(GTK_RANGE(slider->scale), 0.0);
	slider->source = g_timeout_add(3000, _slider_on_timeout, slider);
	return FALSE;
}


/* slider_on_timeout */
static gboolean _slider_on_timeout(gpointer data)
{
	Slider * slider = data;
	LockerAuthHelper * helper = slider->helper;

	slider->source = 0;
	helper->action(helper->locker, LOCKER_ACTION_ACTIVATE);
	return FALSE;
}
