/*============================================================================
Copyright (c) 2024 Raspberry Pi
Some code based on lxinput from the LXDE project :
Copyright (c) 2009-2014 PCMan, martyj19, Julien Lavergne, Andri Grytsenko
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the copyright holder nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <locale.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "rasputin.h"

extern km_functions_t labwc_ifunctions;
extern km_functions_t openbox_ifunctions;

#ifdef PLUGIN_NAME
extern void call_plugin_func (char *name);
#endif

/*----------------------------------------------------------------------------*/
/* Typedefs and macros */
/*----------------------------------------------------------------------------*/

#define TIMEOUT_MS 500

/*----------------------------------------------------------------------------*/
/* Global data */
/*----------------------------------------------------------------------------*/

/* Widgets */
static GtkWidget *mouse_speed, *mouse_dclick, *mouse_left_handed,
    *kb_delay, *kb_interval, *kb_layout, *dclick_btn, *dclick_ind;

/* Setting values */
int dclick, delay, interval;
float speed;
gboolean left_handed;

#ifndef PLUGIN_NAME
/* Setting backups */
static int old_dclick, old_delay, old_interval;
static float old_speed;
static gboolean old_left_handed;
#endif

/* Control timer handles */
static guint dctimer, matimer, kbtimer;

static km_functions_t km_fn;

static GtkBuilder *builder;
static GtkGesture *gesture;
static GdkPixbuf *black, *white;

/*----------------------------------------------------------------------------*/
/* Function prototypes */
/*----------------------------------------------------------------------------*/

static gboolean dclick_handler (gpointer data);
static gboolean speed_handler (gpointer data);
static gboolean kbd_handler (gpointer data);
static void init_config (void);
static gboolean on_mouse_dclick_changed (GtkRange *range, GdkEventButton *event, gpointer user_data);
static gboolean on_mouse_speed_changed (GtkRange *range, GdkEventButton *event, gpointer user_data);
static gboolean on_kb_range_changed (GtkRange *range, GdkEventButton *event, int *val);
static void on_left_handed_toggle (GtkSwitch *btn, gpointer, gpointer user_data);
static void on_set_keyboard_ext (GtkButton *btn, gpointer ptr);
static gboolean reset_indicator (gpointer ptr);
static void on_gpress (GtkGestureMultiPress *self, gint n_press, gdouble x, gdouble y, gpointer ptr);
#ifndef PLUGIN_NAME
static gboolean ok_main (GtkButton *button, gpointer data);
static gboolean cancel_main (GtkButton *button, gpointer data);
static gboolean close_prog (GtkWidget *widget, GdkEvent *event, gpointer data);
#endif

/*----------------------------------------------------------------------------*/
/* Timer handlers */
/*----------------------------------------------------------------------------*/

static gboolean dclick_handler (gpointer data)
{
    km_fn.set_doubleclick ();
    dctimer = 0;
    return FALSE;
}

static gboolean speed_handler (gpointer data)
{
    km_fn.set_speed ();
    matimer = 0;
    return FALSE;
}

static gboolean kbd_handler (gpointer data)
{
    km_fn.set_keyboard ();
    kbtimer = 0;
    return FALSE;
}

/*----------------------------------------------------------------------------*/
/* Widget handlers */
/*----------------------------------------------------------------------------*/

static gboolean on_mouse_dclick_changed (GtkRange *range, GdkEventButton *event, gpointer user_data)
{
    if (dctimer) g_source_remove (dctimer);
    dclick = gtk_range_get_value (range);
    dctimer = g_timeout_add (TIMEOUT_MS, dclick_handler, NULL);
    return FALSE;
}

static gboolean on_mouse_speed_changed (GtkRange *range, GdkEventButton *event, gpointer user_data)
{
    if (matimer) g_source_remove (matimer);
    speed = (gtk_range_get_value (range) / 5.0) - 1.0;
    matimer = g_timeout_add (TIMEOUT_MS, speed_handler, NULL);
    return FALSE;
}

static gboolean on_kb_range_changed (GtkRange *range, GdkEventButton *event, int *val)
{
    if (kbtimer) g_source_remove (kbtimer);
    *val = (int) gtk_range_get_value (range);
    kbtimer = g_timeout_add (TIMEOUT_MS, kbd_handler, NULL);
    return FALSE;
}

static void on_left_handed_toggle (GtkSwitch *btn, gpointer, gpointer user_data)
{
    left_handed = gtk_switch_get_active (btn);
    km_fn.set_lefthanded ();
}

static void on_set_keyboard_ext (GtkButton *btn, gpointer ptr)
{
#ifdef PLUGIN_NAME
    call_plugin_func ("on_set_keyboard");
#else
    g_spawn_command_line_async ("rc_gui -k", NULL);
#endif
}

static gboolean reset_indicator (gpointer ptr)
{
    gtk_image_set_from_pixbuf (GTK_IMAGE (dclick_ind), black);
    return FALSE;
}

static void on_gpress (GtkGestureMultiPress *self, gint n_press, gdouble x, gdouble y, gpointer ptr)
{
    if (n_press == 2)
    {
        g_object_unref (gesture);
        gesture = gtk_gesture_multi_press_new (dclick_btn);
        g_signal_connect (gesture, "pressed", G_CALLBACK (on_gpress), NULL);
        gtk_image_set_from_pixbuf (GTK_IMAGE (dclick_ind), white);
        g_timeout_add (250, G_SOURCE_FUNC (reset_indicator), NULL);
    }
}

/*----------------------------------------------------------------------------*/
/* Initial configuration                                                      */
/*----------------------------------------------------------------------------*/

static void init_config (void)
{
    /* load the current state */
    km_fn.load_config ();

    /* zero timer handles */
    dctimer = 0;
    matimer = 0;
    kbtimer = 0;

    mouse_speed = (GtkWidget *) gtk_builder_get_object (builder, "mouse_speed");
    gtk_range_set_value (GTK_RANGE (mouse_speed), (speed + 1) * 5.0);
    g_signal_connect (mouse_speed, "button-release-event", G_CALLBACK (on_mouse_speed_changed), NULL);

    mouse_dclick = (GtkWidget *) gtk_builder_get_object (builder, "mouse_dclick");
    gtk_range_set_value (GTK_RANGE (mouse_dclick), dclick);
    g_signal_connect (mouse_dclick, "button-release-event", G_CALLBACK (on_mouse_dclick_changed), NULL);

    mouse_left_handed = (GtkWidget *) gtk_builder_get_object (builder, "left_handed");
    gtk_switch_set_active (GTK_SWITCH (mouse_left_handed), left_handed);
    g_signal_connect (mouse_left_handed, "notify::active", G_CALLBACK (on_left_handed_toggle), NULL);

    kb_delay = (GtkWidget *) gtk_builder_get_object (builder, "kb_delay");
    gtk_range_set_value (GTK_RANGE (kb_delay), delay);
    g_signal_connect (kb_delay, "button-release-event", G_CALLBACK (on_kb_range_changed), &delay);

    kb_interval = (GtkWidget *) gtk_builder_get_object (builder, "kb_interval");
    gtk_range_set_value (GTK_RANGE (kb_interval), interval);
    g_signal_connect (kb_interval, "button-release-event", G_CALLBACK (on_kb_range_changed), &interval);

    kb_layout = (GtkWidget *) gtk_builder_get_object (builder, "keyboard_layout");
    g_signal_connect (kb_layout, "clicked", G_CALLBACK (on_set_keyboard_ext), NULL);

    dclick_btn = (GtkWidget *) gtk_builder_get_object (builder, "dclick");
    gesture = gtk_gesture_multi_press_new (dclick_btn);
    g_signal_connect (gesture, "pressed", G_CALLBACK (on_gpress), NULL);
    dclick_ind = (GtkWidget *) gtk_builder_get_object (builder, "dclick_ind");

    black = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 32, 32);
    gdk_pixbuf_fill (black, 0x707070ff);
    white = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 32, 32);
    gdk_pixbuf_fill (white, 0xffffffff);
    gtk_image_set_from_pixbuf (GTK_IMAGE (dclick_ind), black);
}

/*----------------------------------------------------------------------------*/
/* Plugin interface */
/*----------------------------------------------------------------------------*/

#ifdef PLUGIN_NAME

void init_plugin (GtkWidget *)
{
    setlocale (LC_ALL, "");
    bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
    textdomain (GETTEXT_PACKAGE);

    if (getenv ("WAYLAND_DISPLAY")) km_fn = labwc_ifunctions;
    else km_fn = openbox_ifunctions;

    builder = gtk_builder_new_from_file (PACKAGE_DATA_DIR "/ui/rasputin.ui");

    init_config ();
}

int plugin_tabs (void)
{
    return 2;
}

const char *tab_name (int tab)
{
    if (tab == 0) return C_("tab", "Mouse");
    else return C_("tab", "Keyboard");
}

const char *icon_name (int tab)
{
    if (tab == 0) return "input-mouse";
    else return "input-keyboard";
}

const char *tab_id (int tab)
{
    return NULL;
}

GtkWidget *get_tab (int tab)
{
    GtkWidget *window, *plugin;

    window = (GtkWidget *) gtk_builder_get_object (builder, "notebook");
    plugin = (GtkWidget *) gtk_builder_get_object (builder, tab ? "kbd_page" : "mouse_page");

    gtk_container_remove (GTK_CONTAINER (window), plugin);

    return plugin;
}

gboolean reboot_needed (void)
{
    return FALSE;
}

void free_plugin (void)
{
    if (dctimer) g_source_remove (dctimer);
    if (matimer) g_source_remove (matimer);
    if (kbtimer) g_source_remove (kbtimer);
    g_object_unref (builder);
}

#else

/*----------------------------------------------------------------------------*/
/* Main window button handlers                                                */
/*----------------------------------------------------------------------------*/

static gboolean ok_main (GtkButton *button, gpointer data)
{
    gtk_main_quit ();
    return FALSE;
}

static gboolean cancel_main (GtkButton *button, gpointer data)
{
    /* revert to initial state on cancel */
    left_handed = old_left_handed;
    speed = old_speed;
    dclick = old_dclick;
    delay = old_delay;
    interval = old_interval;

    km_fn.set_speed ();
    km_fn.set_doubleclick ();
    km_fn.set_keyboard ();
    km_fn.set_lefthanded ();
    gtk_main_quit ();
    return FALSE;
}

static gboolean close_prog (GtkWidget *widget, GdkEvent *event, gpointer data)
{
    gtk_main_quit ();
    return TRUE;
}

/*----------------------------------------------------------------------------*/
/* Main function */
/*----------------------------------------------------------------------------*/

int main (int argc, char* argv[])
{
    GtkWidget *main_dlg, *wid;

    setlocale (LC_ALL, "");
    bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
    textdomain (GETTEXT_PACKAGE);

    if (getenv ("WAYLAND_DISPLAY")) km_fn = labwc_ifunctions;
    else km_fn = openbox_ifunctions;

    gtk_init (&argc, &argv);

    builder = gtk_builder_new_from_file (PACKAGE_DATA_DIR "/ui/rasputin.ui");

    main_dlg = (GtkWidget *) gtk_builder_get_object (builder, "dlg");
    g_signal_connect (main_dlg, "delete_event", G_CALLBACK (close_prog), NULL);

    wid = (GtkWidget *) gtk_builder_get_object (builder, "button_ok");
    g_signal_connect (wid, "clicked", G_CALLBACK (ok_main), NULL);
    wid = (GtkWidget *) gtk_builder_get_object (builder, "button_cancel");
    g_signal_connect (wid, "clicked", G_CALLBACK (cancel_main), NULL);

    init_config ();

    /* backup the existing state */
    old_left_handed = left_handed;
    old_speed = speed;
    old_dclick = dclick;
    old_delay = delay;
    old_interval = interval;

    g_object_unref (builder);

    gtk_widget_show_all (main_dlg);

    gtk_main ();

    return 0;
}

#endif

/* End of file */
/*============================================================================*/
