#include "otp-button-row.h"

#if !ADW_CHECK_VERSION(1, 6, 0)

struct _OtpButtonRow
{
    AdwPreferencesRow parent_instance;

    GtkWidget *box;
    GtkWidget *start_image;
    GtkWidget *label;
    GtkWidget *end_image;

    gchar *start_icon_name;
    gchar *end_icon_name;

    /* The enclosing GtkListBox we're currently relaying row-activated from.
     * NULL when unparented or when the parent is not a GtkListBox. */
    GtkWidget *previous_parent;
};

enum
{
    PROP_0,
    PROP_START_ICON_NAME,
    PROP_END_ICON_NAME,
    N_PROPS
};

enum
{
    SIGNAL_ACTIVATED,
    N_SIGNALS
};

static GParamSpec *properties[N_PROPS];
static guint       signals[N_SIGNALS];

G_DEFINE_FINAL_TYPE (OtpButtonRow, otp_button_row, ADW_TYPE_PREFERENCES_ROW)

static void
ensure_css_provider (void)
{
    static gsize loaded = 0;
    if (g_once_init_enter (&loaded))
    {
        GtkCssProvider *provider = gtk_css_provider_new ();
        gtk_css_provider_load_from_resource (provider,
                                             "/com/github/paolostivanin/OTPClient/ui/otp-button-row.css");
        gtk_style_context_add_provider_for_display (gdk_display_get_default (),
                                                    GTK_STYLE_PROVIDER (provider),
                                                    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
        g_object_unref (provider);
        g_once_init_leave (&loaded, 1);
    }
}

static void
update_start_icon (OtpButtonRow *self)
{
    if (self->start_icon_name != NULL && self->start_icon_name[0] != '\0')
    {
        gtk_image_set_from_icon_name (GTK_IMAGE (self->start_image), self->start_icon_name);
        gtk_widget_set_visible (self->start_image, TRUE);
    }
    else
    {
        gtk_widget_set_visible (self->start_image, FALSE);
    }
}

static void
update_end_icon (OtpButtonRow *self)
{
    if (self->end_icon_name != NULL && self->end_icon_name[0] != '\0')
    {
        gtk_image_set_from_icon_name (GTK_IMAGE (self->end_image), self->end_icon_name);
        gtk_widget_set_visible (self->end_image, TRUE);
    }
    else
    {
        gtk_widget_set_visible (self->end_image, FALSE);
    }
}

/* GtkListBoxRow::activate is NOT called by GtkListBox on user click - it's an
 * action signal a caller can fire to simulate activation. To re-emit our
 * "activated" signal on a real click, we mirror AdwButtonRow 1.6: listen for
 * notify::parent, connect_swapped to the parent GtkListBox's "row-activated",
 * and emit when the activated row is us. */
static void
row_activated_cb (OtpButtonRow  *self,
                  GtkListBoxRow *row)
{
    if ((GtkListBoxRow *) self == row)
        g_signal_emit (self, signals[SIGNAL_ACTIVATED], 0);
}

static void
parent_cb (OtpButtonRow *self)
{
    GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (self));

    if (self->previous_parent != NULL) {
        g_signal_handlers_disconnect_by_func (self->previous_parent,
                                              G_CALLBACK (row_activated_cb), self);
        self->previous_parent = NULL;
    }

    if (parent == NULL || !GTK_IS_LIST_BOX (parent))
        return;

    self->previous_parent = parent;
    g_signal_connect_swapped (parent, "row-activated", G_CALLBACK (row_activated_cb), self);
}

static void
otp_button_row_dispose (GObject *object)
{
    OtpButtonRow *self = OTP_BUTTON_ROW (object);

    if (self->previous_parent != NULL) {
        g_signal_handlers_disconnect_by_func (self->previous_parent,
                                              G_CALLBACK (row_activated_cb), self);
        self->previous_parent = NULL;
    }

    G_OBJECT_CLASS (otp_button_row_parent_class)->dispose (object);
}

static void
otp_button_row_finalize (GObject *object)
{
    OtpButtonRow *self = OTP_BUTTON_ROW (object);

    g_clear_pointer (&self->start_icon_name, g_free);
    g_clear_pointer (&self->end_icon_name, g_free);

    G_OBJECT_CLASS (otp_button_row_parent_class)->finalize (object);
}

static void
otp_button_row_get_property (GObject    *object,
                             guint       prop_id,
                             GValue     *value,
                             GParamSpec *pspec)
{
    OtpButtonRow *self = OTP_BUTTON_ROW (object);

    switch (prop_id)
    {
        case PROP_START_ICON_NAME:
            g_value_set_string (value, self->start_icon_name);
            break;
        case PROP_END_ICON_NAME:
            g_value_set_string (value, self->end_icon_name);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
otp_button_row_set_property (GObject      *object,
                             guint         prop_id,
                             const GValue *value,
                             GParamSpec   *pspec)
{
    OtpButtonRow *self = OTP_BUTTON_ROW (object);

    switch (prop_id)
    {
        case PROP_START_ICON_NAME:
            otp_button_row_set_start_icon_name (self, g_value_get_string (value));
            break;
        case PROP_END_ICON_NAME:
            otp_button_row_set_end_icon_name (self, g_value_get_string (value));
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
otp_button_row_class_init (OtpButtonRowClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->dispose      = otp_button_row_dispose;
    object_class->finalize     = otp_button_row_finalize;
    object_class->get_property = otp_button_row_get_property;
    object_class->set_property = otp_button_row_set_property;

    properties[PROP_START_ICON_NAME] =
        g_param_spec_string ("start-icon-name", NULL, NULL, NULL,
                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
    properties[PROP_END_ICON_NAME] =
        g_param_spec_string ("end-icon-name", NULL, NULL, NULL,
                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);

    g_object_class_install_properties (object_class, N_PROPS, properties);

    signals[SIGNAL_ACTIVATED] =
        g_signal_new ("activated",
                      G_TYPE_FROM_CLASS (klass),
                      G_SIGNAL_RUN_LAST,
                      0, NULL, NULL, NULL,
                      G_TYPE_NONE, 0);
}

static void
otp_button_row_init (OtpButtonRow *self)
{
    ensure_css_provider ();

    gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (self), TRUE);
    gtk_widget_add_css_class (GTK_WIDGET (self), "button-row");

    self->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
    gtk_widget_add_css_class (self->box, "header");

    self->start_image = gtk_image_new ();
    gtk_widget_set_visible (self->start_image, FALSE);
    gtk_box_append (GTK_BOX (self->box), self->start_image);

    self->label = gtk_label_new (NULL);
    gtk_widget_set_hexpand (self->label, TRUE);
    gtk_label_set_xalign (GTK_LABEL (self->label), 0.5);
    gtk_label_set_ellipsize (GTK_LABEL (self->label), PANGO_ELLIPSIZE_END);
    gtk_box_append (GTK_BOX (self->box), self->label);

    self->end_image = gtk_image_new ();
    gtk_widget_set_visible (self->end_image, FALSE);
    gtk_box_append (GTK_BOX (self->box), self->end_image);

    gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (self), self->box);

    g_object_bind_property (self, "title",
                            self->label, "label",
                            G_BINDING_SYNC_CREATE);

    g_signal_connect (self, "notify::parent", G_CALLBACK (parent_cb), NULL);
}

GtkWidget *
otp_button_row_new (void)
{
    return g_object_new (OTP_TYPE_BUTTON_ROW, NULL);
}

const gchar *
otp_button_row_get_start_icon_name (OtpButtonRow *self)
{
    g_return_val_if_fail (OTP_IS_BUTTON_ROW (self), NULL);
    return self->start_icon_name;
}

void
otp_button_row_set_start_icon_name (OtpButtonRow *self,
                                    const gchar  *icon_name)
{
    g_return_if_fail (OTP_IS_BUTTON_ROW (self));

    if (g_strcmp0 (self->start_icon_name, icon_name) == 0)
        return;

    g_free (self->start_icon_name);
    self->start_icon_name = g_strdup (icon_name);
    update_start_icon (self);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START_ICON_NAME]);
}

const gchar *
otp_button_row_get_end_icon_name (OtpButtonRow *self)
{
    g_return_val_if_fail (OTP_IS_BUTTON_ROW (self), NULL);
    return self->end_icon_name;
}

void
otp_button_row_set_end_icon_name (OtpButtonRow *self,
                                  const gchar  *icon_name)
{
    g_return_if_fail (OTP_IS_BUTTON_ROW (self));

    if (g_strcmp0 (self->end_icon_name, icon_name) == 0)
        return;

    g_free (self->end_icon_name);
    self->end_icon_name = g_strdup (icon_name);
    update_end_icon (self);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_END_ICON_NAME]);
}

#endif /* !ADW_CHECK_VERSION(1, 6, 0) */
