/* PIKA - Photo and Image Kooker Application
 * a rebranding of The GNU Image Manipulation Program (created with heckimp)
 * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
 *
 * Original copyright, applying to most contents (license remains unchanged): 
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
 *
 * 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 <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>

#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikaconfig/pikaconfig.h"
#include "libpikamath/pikamath.h"
#include "libpikawidgets/pikawidgets.h"

#include "display-types.h"

#include "config/pikacoreconfig.h"

#include "gegl/pika-babl.h"

#include "core/pikaimage.h"
#include "core/pikaprojectable.h"

#include "pikadisplay.h"
#include "pikadisplayshell.h"
#include "pikadisplayshell-actions.h"
#include "pikadisplayshell-filter.h"
#include "pikadisplayshell-profile.h"

#include "pika-intl.h"


/*  local function prototypes  */

static void   pika_display_shell_profile_free        (PikaDisplayShell *shell);

static void   pika_display_shell_color_config_notify (PikaColorConfig  *config,
                                                      const GParamSpec *pspec,
                                                      PikaDisplayShell *shell);


/*  public functions  */

void
pika_display_shell_profile_init (PikaDisplayShell *shell)
{
  PikaColorConfig *color_config;

  color_config = PIKA_CORE_CONFIG (shell->display->config)->color_management;

  shell->color_config = pika_config_duplicate (PIKA_CONFIG (color_config));

  /* use after so we are called after the profile cache is invalidated
   * in pika_widget_get_color_transform()
   */
  g_signal_connect_after (shell->color_config, "notify",
                          G_CALLBACK (pika_display_shell_color_config_notify),
                          shell);
}

void
pika_display_shell_profile_finalize (PikaDisplayShell *shell)
{
  g_clear_object (&shell->color_config);

  pika_display_shell_profile_free (shell);
}

void
pika_display_shell_profile_update (PikaDisplayShell *shell)
{
  PikaImage               *image;
  PikaColorProfile        *src_profile;
  const Babl              *src_format;
  PikaColorProfile        *filter_profile;
  const Babl              *filter_format;
  const Babl              *dest_format;
  PikaColorProfile        *proof_profile;
  PikaColorRenderingIntent simulation_intent =
    PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC;
  gboolean                 simulation_bpc    = FALSE;

  pika_display_shell_profile_free (shell);

  image = pika_display_get_image (shell->display);

  if (! image)
    return;

  src_profile = pika_color_managed_get_color_profile (PIKA_COLOR_MANAGED (shell));

  if (! src_profile)
    return;

  proof_profile = pika_color_managed_get_simulation_profile (PIKA_COLOR_MANAGED (image));
  simulation_intent = pika_color_managed_get_simulation_intent (PIKA_COLOR_MANAGED (image));
  simulation_bpc = pika_color_managed_get_simulation_bpc (PIKA_COLOR_MANAGED (image));

  src_format = pika_projectable_get_format (PIKA_PROJECTABLE (image));

  if (pika_display_shell_has_filter (shell))
    {
      filter_format  = shell->filter_format;
      filter_profile = shell->filter_profile;
    }
  else
    {
      filter_format  = src_format;
      filter_profile = src_profile;
    }

  if (! pika_display_shell_profile_can_convert_to_u8 (shell))
    {
      dest_format = shell->filter_format;
    }
  else
    {
      dest_format = babl_format ("R'G'B'A u8");
    }

#if 0
  g_printerr ("src_profile:    %s\n"
              "src_format:     %s\n"
              "filter_profile: %s\n"
              "filter_format:  %s\n"
              "dest_format:    %s\n",
              pika_color_profile_get_label (src_profile),
              babl_get_name (src_format),
              pika_color_profile_get_label (filter_profile),
              babl_get_name (filter_format),
              babl_get_name (dest_format));
#endif

  if (! pika_color_transform_can_gegl_copy (src_profile, filter_profile))
    {
      shell->filter_transform =
        pika_color_transform_new (src_profile,
                                  src_format,
                                  filter_profile,
                                  filter_format,
                                  PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
                                  PIKA_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION |
                                  PIKA_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE);
    }

  shell->profile_transform =
    pika_widget_get_color_transform (gtk_widget_get_toplevel (GTK_WIDGET (shell)),
                                     pika_display_shell_get_color_config (shell),
                                     filter_profile,
                                     filter_format,
                                     dest_format,
                                     proof_profile,
                                     simulation_intent,
                                     simulation_bpc);

  if (shell->filter_transform || shell->profile_transform)
    {
      gint w = shell->render_buf_width;
      gint h = shell->render_buf_height;

      shell->profile_data =
        gegl_malloc (w * h * babl_format_get_bytes_per_pixel (src_format));

      shell->profile_stride =
        w * babl_format_get_bytes_per_pixel (src_format);

      shell->profile_buffer =
        gegl_buffer_linear_new_from_data (shell->profile_data,
                                          src_format,
                                          GEGL_RECTANGLE (0, 0, w, h),
                                          GEGL_AUTO_ROWSTRIDE,
                                          (GDestroyNotify) gegl_free,
                                          shell->profile_data);
    }
}

gboolean
pika_display_shell_profile_can_convert_to_u8 (PikaDisplayShell *shell)
{
  PikaImage *image = pika_display_get_image (shell->display);

  if (image)
    {
      PikaComponentType component_type;

      if (! pika_display_shell_has_filter (shell))
        component_type = pika_image_get_component_type (image);
      else
        component_type = pika_babl_format_get_component_type (shell->filter_format);

      switch (component_type)
        {
        case PIKA_COMPONENT_TYPE_U8:
#if 0
          /* would like to convert directly for these too, but it
           * produces inferior results, see bug 750874
           */
        case PIKA_COMPONENT_TYPE_U16:
        case PIKA_COMPONENT_TYPE_U32:
#endif
          return TRUE;

        default:
          break;
        }
    }

  return FALSE;
}


/*  private functions  */

static void
pika_display_shell_profile_free (PikaDisplayShell *shell)
{
  g_clear_object (&shell->profile_transform);
  g_clear_object (&shell->filter_transform);
  g_clear_object (&shell->profile_buffer);
  shell->profile_data   = NULL;
  shell->profile_stride = 0;
}

static void
pika_display_shell_color_config_notify (PikaColorConfig  *config,
                                        const GParamSpec *pspec,
                                        PikaDisplayShell *shell)
{
  if (! strcmp (pspec->name, "mode")                                    ||
      ! strcmp (pspec->name, "display-rendering-intent")                ||
      ! strcmp (pspec->name, "display-use-black-point-compensation")    ||
      ! strcmp (pspec->name, "simulation-gamut-check"))
    {
      gboolean     managed   = FALSE;
      gboolean     softproof = FALSE;
      const gchar *action    = NULL;

#define SET_SENSITIVE(action, sensitive) \
      pika_display_shell_set_action_sensitive (shell, action, sensitive);

#define SET_ACTIVE(action, active) \
      pika_display_shell_set_action_active (shell, action, active);

      switch (pika_color_config_get_mode (config))
        {
        case PIKA_COLOR_MANAGEMENT_OFF:
          break;

        case PIKA_COLOR_MANAGEMENT_DISPLAY:
          managed = TRUE;
          break;

        case PIKA_COLOR_MANAGEMENT_SOFTPROOF:
          managed   = TRUE;
          softproof = TRUE;
          break;
        }

      SET_ACTIVE ("view-color-management-enable", managed);
      SET_ACTIVE ("view-color-management-softproof", softproof);

      switch (pika_color_config_get_display_intent (config))
        {
        case PIKA_COLOR_RENDERING_INTENT_PERCEPTUAL:
          action = "view-display-intent-perceptual";
          break;

        case PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC:
          action = "view-display-intent-relative-colorimetric";
          break;

        case PIKA_COLOR_RENDERING_INTENT_SATURATION:
          action = "view-display-intent-saturation";
          break;

        case PIKA_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC:
          action = "view-display-intent-absolute-colorimetric";
          break;
        }

      SET_SENSITIVE ("view-display-intent-perceptual",            managed);
      SET_SENSITIVE ("view-display-intent-relative-colorimetric", managed);
      SET_SENSITIVE ("view-display-intent-saturation",            managed);
      SET_SENSITIVE ("view-display-intent-absolute-colorimetric", managed);

      SET_ACTIVE (action, TRUE);

      SET_SENSITIVE ("view-display-black-point-compensation", managed);
      SET_ACTIVE    ("view-display-black-point-compensation",
                     pika_color_config_get_display_bpc (config));
      SET_SENSITIVE ("view-softproof-gamut-check", softproof);
      SET_ACTIVE    ("view-softproof-gamut-check",
                     pika_color_config_get_simulation_gamut_check (config));
    }

  pika_color_managed_profile_changed (PIKA_COLOR_MANAGED (shell));
}
