/** \file
 * \brief Valuator Control for GTK4
 *
 * See Copyright Notice in "iup.h"
 */

#include <stdlib.h>

#include "iup.h"
#include "iupcbs.h"

#include "iup_object.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_val.h"

#include "iupgtk4_drv.h"


IUP_SDK_API void iupdrvValGetMinSize(Ihandle* ih, int *w, int *h)
{
  static int horiz_min_w = -1, horiz_min_h = -1;
  static int vert_min_w = -1, vert_min_h = -1;

  /* Measure the minimum sizes required by GtkScale */
  if (horiz_min_w < 0)
  {
    GtkAdjustment* adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 1.0, 0.01, 0.1, 0));
    GtkWidget* temp_horiz = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, adj);
    GtkWidget* temp_vert = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adj);

    gtk_widget_measure(temp_horiz, GTK_ORIENTATION_HORIZONTAL, -1, &horiz_min_w, NULL, NULL, NULL);
    gtk_widget_measure(temp_horiz, GTK_ORIENTATION_VERTICAL, -1, &horiz_min_h, NULL, NULL, NULL);
    gtk_widget_measure(temp_vert, GTK_ORIENTATION_HORIZONTAL, -1, &vert_min_w, NULL, NULL, NULL);
    gtk_widget_measure(temp_vert, GTK_ORIENTATION_VERTICAL, -1, &vert_min_h, NULL, NULL, NULL);

    g_object_ref_sink(temp_horiz);
    g_object_unref(temp_horiz);
    g_object_ref_sink(temp_vert);
    g_object_unref(temp_vert);

    /* GtkScale's natural long-axis is just the thumb size; widen to a usable default */
    if (horiz_min_w < 100) horiz_min_w = 100;
    if (vert_min_h < 100)  vert_min_h = 100;
  }

  if (ih->data->orientation == IVAL_HORIZONTAL)
  {
    *w = horiz_min_w;
    *h = horiz_min_h;
  }
  else
  {
    *w = vert_min_w;
    *h = vert_min_h;
  }
}

static int gtk4ValSetStepAttrib(Ihandle* ih, const char* value)
{
  if (iupStrToDoubleDef(value, &(ih->data->step), 0.01))
    gtk_range_set_increments(GTK_RANGE(ih->handle), ih->data->step, ih->data->pagestep);
  return 0;
}

static int gtk4ValSetPageStepAttrib(Ihandle* ih, const char* value)
{
  if (iupStrToDoubleDef(value, &(ih->data->pagestep), 0.1))
    gtk_range_set_increments(GTK_RANGE(ih->handle), ih->data->step, ih->data->pagestep);
  return 0;
}

static int gtk4ValSetValueAttrib(Ihandle* ih, const char* value)
{
  if (iupStrToDouble(value, &(ih->data->val)))
  {
    double range = ih->data->vmax - ih->data->vmin;
    if (range != 0)
    {
      double fval = (ih->data->val - ih->data->vmin) / range;
      gtk_range_set_value(GTK_RANGE(ih->handle), fval);
    }
  }
  return 0;
}

static gboolean gtk4ValChangeValue(GtkRange *range, GtkScrollType scroll, double fval, Ihandle *ih)
{
  double old_val = ih->data->val;
  IFn cb;

  if (fval < 0.0)
    gtk_range_set_value(GTK_RANGE(ih->handle), 0.0);
  if (fval > 1.0)
    gtk_range_set_value(GTK_RANGE(ih->handle), 1.0);

  ih->data->val = fval*(ih->data->vmax - ih->data->vmin) + ih->data->vmin;
  iupValCropValue(ih);

  cb = (IFn)IupGetCallback(ih, "VALUECHANGED_CB");
  if (cb)
  {
    if (ih->data->val == old_val)
      return FALSE;

    cb(ih);
  }

  if (fval < 0.0 || fval > 1.0)
    return TRUE;

  (void)range;
  (void)scroll;
  return FALSE;
}

static gboolean gtk4ValKeyPressEvent(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, Ihandle *ih)
{
  (void)controller;
  (void)keycode;
  (void)state;

  if (ih->data->inverted)
  {
    if (keyval==GDK_KEY_Home || keyval==GDK_KEY_KP_Home)
    {
      gtk_range_set_value(GTK_RANGE(ih->handle), 1.0);
      gtk4ValChangeValue(GTK_RANGE(ih->handle), GTK_SCROLL_START, 1.0, ih);
      return TRUE;
    }
    if (keyval==GDK_KEY_End || keyval==GDK_KEY_KP_End)
    {
      gtk_range_set_value(GTK_RANGE(ih->handle), 0.0);
      gtk4ValChangeValue(GTK_RANGE(ih->handle), GTK_SCROLL_END, 0.0, ih);
      return TRUE;
    }
  }

  return FALSE;
}

static int gtk4ValMapMethod(Ihandle* ih)
{
  GtkAdjustment* adjustment = GTK_ADJUSTMENT(gtk_adjustment_new (0, 0, 1.0, 0.01, 0.1, 0));
  if (!adjustment)
    return IUP_ERROR;

  if (ih->data->orientation == IVAL_HORIZONTAL)
    ih->handle = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, adjustment);
  else
    ih->handle = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adjustment);

  if (!ih->handle)
    return IUP_ERROR;

  iupgtk4AddToParent(ih);

  if (!iupAttribGetBoolean(ih, "CANFOCUS"))
    iupgtk4SetCanFocus(ih->handle, 0);

  iupgtk4SetupEnterLeaveEvents(ih->handle, ih);
  iupgtk4SetupFocusEvents(ih->handle, ih);

  GtkEventController* key_controller = gtk_event_controller_key_new();
  gtk_widget_add_controller(ih->handle, key_controller);
  g_signal_connect(key_controller, "key-pressed", G_CALLBACK(gtk4ValKeyPressEvent), ih);

  iupgtk4SetupKeyEvents(ih->handle, ih);

  g_signal_connect(G_OBJECT(ih->handle), "change-value",  G_CALLBACK(gtk4ValChangeValue),  ih);

  gtk_scale_set_draw_value(GTK_SCALE(ih->handle), FALSE);
  gtk_range_set_range(GTK_RANGE(ih->handle), 0.0, 1.0);

  if (ih->data->inverted)
    gtk_range_set_inverted(GTK_RANGE(ih->handle), TRUE);

  gtk_widget_realize(ih->handle);

  iupgtk4UpdateMnemonic(ih);

  return IUP_NOERROR;
}

static void gtk4ValUpdateTicks(Ihandle* ih, int num_ticks)
{
  int i, both = 0;
  GtkPositionType pos, pos2 = GTK_POS_BOTTOM;
  char* tickspos;

  gtk_scale_clear_marks(GTK_SCALE(ih->handle));

  if (num_ticks < 2)
    return;

  tickspos = iupAttribGet(ih, "TICKSPOS");
  both = iupStrEqualNoCase(tickspos, "BOTH");
  if (ih->data->orientation == IVAL_HORIZONTAL)
  {
    pos  = (iupStrEqualNoCase(tickspos, "REVERSE")) ? GTK_POS_TOP : GTK_POS_BOTTOM;
    pos2 = GTK_POS_TOP;
  }
  else
  {
    pos  = (iupStrEqualNoCase(tickspos, "REVERSE")) ? GTK_POS_LEFT : GTK_POS_RIGHT;
    pos2 = GTK_POS_LEFT;
  }

  for (i = 0; i < num_ticks; i++)
  {
    double fval = (double)i / (double)(num_ticks - 1);
    gtk_scale_add_mark(GTK_SCALE(ih->handle), fval, pos, NULL);
    if (both)
      gtk_scale_add_mark(GTK_SCALE(ih->handle), fval, pos2, NULL);
  }
}

static int gtk4ValSetShowTicksAttrib(Ihandle* ih, const char* value)
{
  int num_ticks;

  if (!ih->handle)
    return 1;

  if (!iupStrToInt(value, &num_ticks))
    num_ticks = 0;

  gtk4ValUpdateTicks(ih, num_ticks);

  return 0;
}

static int gtk4ValSetTicksPosAttrib(Ihandle* ih, const char* value)
{
  int num_ticks;

  if (!ih->handle)
    return 1;

  (void)value;

  num_ticks = iupAttribGetInt(ih, "SHOWTICKS");
  if (num_ticks > 1)
    gtk4ValUpdateTicks(ih, num_ticks);

  return 0;
}

IUP_SDK_API void iupdrvValInitClass(Iclass* ic)
{
  ic->Map = gtk4ValMapMethod;

  iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT);

  iupClassRegisterAttribute(ic, "VALUE", iupValGetValueAttrib, gtk4ValSetValueAttrib, IUPAF_SAMEASSYSTEM, "0", IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "PAGESTEP", iupValGetPageStepAttrib, gtk4ValSetPageStepAttrib, NULL, NULL, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "STEP", iupValGetStepAttrib, gtk4ValSetStepAttrib, NULL, NULL, IUPAF_NO_INHERIT);

  iupClassRegisterAttribute(ic, "SHOWTICKS", iupValGetShowTicksAttrib, gtk4ValSetShowTicksAttrib, IUPAF_SAMEASSYSTEM, "0", IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "TICKSPOS", NULL, gtk4ValSetTicksPosAttrib, "NORMAL", NULL, IUPAF_DEFAULT);
}
