/* ide-notification-view.c
 *
 * Copyright 2018-2019 Christian Hergert <chergert@redhat.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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#define G_LOG_DOMAIN "ide-notification-view"

#include "config.h"

#include <dazzle.h>

#include "ide-notification-view-private.h"

struct _IdeNotificationView
{
  GtkBin           parent_instance;

  IdeNotification *notification;
  DzlBindingGroup *bindings;

  GtkLabel        *label;
  GtkBox          *buttons;
  GtkButton       *default_button;
  GtkImage        *default_button_image;
};

G_DEFINE_TYPE (IdeNotificationView, ide_notification_view, GTK_TYPE_BIN)

enum {
  PROP_0,
  PROP_NOTIFICATION,
  N_PROPS
};

static GParamSpec *properties [N_PROPS];

static void
ide_notification_view_notify_icon (IdeNotificationView *self,
                                   GParamSpec          *pspec,
                                   IdeNotification     *notif)
{
  g_autoptr(GIcon) icon = NULL;

  g_assert (IDE_IS_MAIN_THREAD ());
  g_assert (IDE_IS_NOTIFICATION_VIEW (self));
  g_assert (IDE_IS_NOTIFICATION (notif));

  icon = ide_notification_ref_icon (notif);
  gtk_image_set_from_gicon (self->default_button_image, icon, GTK_ICON_SIZE_MENU);
  gtk_widget_set_visible (GTK_WIDGET (self->default_button), icon != NULL);
}

static void
connect_notification (IdeNotificationView *self,
                      IdeNotification     *notification)
{
  g_autofree gchar *action_name = NULL;
  g_autoptr(GVariant) target_value = NULL;
  g_autoptr(GIcon) icon = NULL;
  guint n_buttons;

  g_assert (IDE_IS_NOTIFICATION_VIEW (self));
  g_assert (!notification || IDE_IS_NOTIFICATION (notification));

  gtk_container_foreach (GTK_CONTAINER (self->buttons), (GtkCallback)gtk_widget_destroy, NULL);

  if (notification == NULL)
    {
      gtk_widget_hide (GTK_WIDGET (self->label));
      gtk_widget_hide (GTK_WIDGET (self->default_button));
      gtk_widget_hide (GTK_WIDGET (self->buttons));
      return;
    }

  g_signal_connect_object (notification,
                           "notify::icon",
                           G_CALLBACK (ide_notification_view_notify_icon),
                           self,
                           G_CONNECT_SWAPPED);
  ide_notification_view_notify_icon (self, NULL, notification);

  /*
   * Setup the default action button (which is shown right after the label
   * containing notification title).
   */

  if (ide_notification_get_default_action (notification, &action_name, &target_value))
    {
      gtk_actionable_set_action_name (GTK_ACTIONABLE (self->default_button), action_name);
      gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->default_button), target_value);
    }

  /*
   * Now add all of the buttons requested by the notification.
   */

  ide_object_lock (IDE_OBJECT (notification));

  n_buttons = ide_notification_get_n_buttons (notification);

  for (guint i = 0; i < n_buttons; i++)
    {
      g_autofree gchar *action = NULL;
      g_autofree gchar *label = NULL;
      g_autoptr(GIcon) button_icon = NULL;
      g_autoptr(GVariant) target = NULL;

      if (ide_notification_get_button (notification, i, &label, &button_icon, &action, &target) &&
          button_icon != NULL &&
          action_name != NULL)
        {
          GtkButton *button;

          button = g_object_new (GTK_TYPE_BUTTON,
                                 "child", g_object_new (GTK_TYPE_IMAGE,
                                                        "gicon", button_icon,
                                                        "visible", TRUE,
                                                        NULL),
                                 "action-name", action,
                                 "action-target", target,
                                 "has-tooltip", TRUE,
                                 "tooltip-text", label,
                                 "visible", TRUE,
                                 NULL);
          gtk_container_add (GTK_CONTAINER (self->buttons), GTK_WIDGET (button));
        }
    }

  ide_object_unlock (IDE_OBJECT (notification));
}

static void
ide_notification_view_finalize (GObject *object)
{
  IdeNotificationView *self = (IdeNotificationView *)object;

  g_assert (IDE_IS_MAIN_THREAD ());

  g_clear_object (&self->bindings);
  g_clear_object (&self->notification);

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

static void
ide_notification_view_get_property (GObject    *object,
                                    guint       prop_id,
                                    GValue     *value,
                                    GParamSpec *pspec)
{
  IdeNotificationView *self = IDE_NOTIFICATION_VIEW (object);

  switch (prop_id)
    {
    case PROP_NOTIFICATION:
      g_value_set_object (value, ide_notification_view_get_notification (self));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
ide_notification_view_set_property (GObject      *object,
                                    guint         prop_id,
                                    const GValue *value,
                                    GParamSpec   *pspec)
{
  IdeNotificationView *self = IDE_NOTIFICATION_VIEW (object);

  switch (prop_id)
    {
    case PROP_NOTIFICATION:
      ide_notification_view_set_notification (self, g_value_get_object (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
ide_notification_view_class_init (IdeNotificationViewClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  object_class->finalize = ide_notification_view_finalize;
  object_class->get_property = ide_notification_view_get_property;
  object_class->set_property = ide_notification_view_set_property;

  /**
   * IdeNotificationView:notification:
   *
   * The "notification" property is the #IdeNotification to be displayed.
   *
   * Since: 3.32
   */
  properties [PROP_NOTIFICATION] =
    g_param_spec_object ("notification",
                         "Notification",
                         "The IdeNotification to be viewed",
                         IDE_TYPE_NOTIFICATION,
                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));

  g_object_class_install_properties (object_class, N_PROPS, properties);

  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-notification-view.ui");
  gtk_widget_class_set_css_name (widget_class, "notification");
  gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, label);
  gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, buttons);
  gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, default_button);
  gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, default_button_image);
}

static void
ide_notification_view_init (IdeNotificationView *self)
{
  gtk_widget_init_template (GTK_WIDGET (self));

  self->bindings = dzl_binding_group_new ();

  dzl_binding_group_bind (self->bindings, "title", self->label, "label", G_BINDING_SYNC_CREATE);
}

/**
 * ide_notification_view_new:
 *
 * Create a new #IdeNotificationView to visualize a notification within
 * the #IdeOmniBar.
 *
 * Returns: (transfer full): a newly created #IdeNotificationView
 *
 * Since: 3.32
 */
GtkWidget *
ide_notification_view_new (void)
{
  return g_object_new (IDE_TYPE_NOTIFICATION_VIEW, NULL);
}

/**
 * ide_notification_view_get_notification:
 * @self: an #IdeNotificationView
 *
 * Gets the #IdeNotification that is being viewed.
 *
 * Returns: (transfer none) (nullable): an #IdeNotification or %NULL
 *
 * Since: 3.32
 */
IdeNotification *
ide_notification_view_get_notification (IdeNotificationView *self)
{
  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
  g_return_val_if_fail (IDE_IS_NOTIFICATION_VIEW (self), NULL);

  return self->notification;
}

void
ide_notification_view_set_notification (IdeNotificationView *self,
                                        IdeNotification     *notification)
{
  g_return_if_fail (IDE_IS_MAIN_THREAD ());
  g_return_if_fail (IDE_IS_NOTIFICATION_VIEW (self));
  g_return_if_fail (!notification || IDE_IS_NOTIFICATION (notification));

  if (g_set_object (&self->notification, notification))
    {
      dzl_binding_group_set_source (self->bindings, notification);
      connect_notification (self, notification);
      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NOTIFICATION]);
    }
}
