#!/usr/bin/env python3
#
# 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/>.

import gi
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('GimpUi', '3.0')
from gi.repository import GimpUi
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gio
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import sys

def N_(message): return message
def _(message): return GLib.dgettext(None, message)


def make_gradient(palette, num_segments, num_colors):

    # name the gradient same as the source palette
    # For now, the name of a resource is the same as the ID
    palette_name = palette.get_name()
    gradient = Gimp.Gradient.new(palette_name)
    # assert gradient is valid but is has only one segment
    assert gradient.get_number_of_segments() == 1

    # split one segment into desired count
    # index is zero-based
    gradient.segment_range_split_uniform(0, 0, num_segments)

    palette_colors = palette.get_colors()
    for color_number in range(0, num_segments):
        if color_number == num_colors - 1:
            color_number_next = 0
        else:
            color_number_next = color_number + 1
        color_left = palette_colors[color_number]
        color_right = palette_colors[color_number_next]
        gradient.segment_set_left_color(color_number, color_left)
        gradient.segment_set_right_color(color_number, color_right)

    # Side effects on the context. Probably not what most would expect.
    Gimp.context_set_gradient(gradient)
    return gradient

def run(procedure, config, data):
    # Get the parameters
    run_mode = config.get_property("run-mode")

    if run_mode == Gimp.RunMode.INTERACTIVE:
        GimpUi.init(procedure.get_name())
        dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config)

        # Add palette button
        config.set_property("palette", None)
        dialog.fill (["palette"])

        if not dialog.run():
            dialog.destroy()
            return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())

        dialog.destroy()

    palette = config.get_property("palette")
    if palette is None or not palette.is_valid():
        if palette is not None:
          sys.stderr.write(f'Invalid palette id: {palette.get_id()}\n')
          sys.stderr.write('This should not happen. Please report to GIMP project.\n')
          sys.stderr.write('Falling back to context palette instead.\n')
        palette = Gimp.context_get_palette()
        config.set_property("palette", palette)

    num_colors = palette.get_color_count()

    if procedure.get_name() == 'python-fu-palette-to-gradient':
        num_segments = num_colors - 1
    else: # 'python-fu-palette-to-gradient-repeating'
        num_segments = num_colors
    gradient = make_gradient(palette, num_segments, num_colors)

    # XXX: for the error parameter, we want to return None.
    # Unfortunately even though the argument is (nullable), pygobject
    # looks like it may have a bug. So workaround is to just set a
    # generic GLib.Error() since anyway the error won't be process with
    # GIMP_PDB_SUCCESS status.
    # See pygobject#351
    retval = procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())

    # XXX: I don't try to get the GValue with retval.index(1) because it
    # actually return a string (cf. pygobject#353). Just create a new
    # GValue and replace the default one with this one.
    # This comment dates from when resources are strings:
    # value = GObject.Value(GObject.TYPE_STRING, gradient)

    value = GObject.Value(Gimp.Gradient, gradient)
    retval.remove(1)
    retval.insert(1, value)

    return retval

class PaletteToGradient (Gimp.PlugIn):
    ## GimpPlugIn virtual methods ##
    def do_set_i18n(self, procname):
        return True, 'gimp30-python', None

    def do_query_procedures(self):
        return ['python-fu-palette-to-gradient',
                'python-fu-palette-to-gradient-repeating']

    def do_create_procedure(self, name):
        procedure = Gimp.Procedure.new(self, name,
                                       Gimp.PDBProcType.PLUGIN,
                                       run, None)
        if name == 'python-fu-palette-to-gradient':
            procedure.set_menu_label(_("Palette to _Gradient"))
            procedure.set_documentation(_("Create a gradient using colors from the palette"),
                                        _("Create a new gradient using colors from the palette."),
                                        "")
        elif name == 'python-fu-palette-to-gradient-repeating':
            procedure.set_menu_label(_("Palette to _Repeating Gradient"))
            procedure.set_documentation(_("Create a repeating gradient using colors from the palette"),
                                        _("Create a new repeating gradient using colors from the palette."),
                                        "")
        else:
            procedure = None

        if procedure is not None:
            procedure.set_attribution("Carol Spears, reproduced from previous work by Adrian Likins and Jeff Trefftz",
                                      "Carol Spears", "2006")

            procedure.add_enum_argument ("run-mode", _("Run mode"),
                                         _("The run mode"), Gimp.RunMode,
                                         Gimp.RunMode.NONINTERACTIVE,
                                         GObject.ParamFlags.READWRITE)
            procedure.add_palette_argument ("palette", _("_Palette"),
                                            _("Palette"), True,
                                            None, True, # Default to context.
                                            GObject.ParamFlags.READWRITE)
            procedure.add_gradient_return_value ("new-gradient", _("The newly created gradient"),
                                                 _("The newly created gradient"),
                                                 GObject.ParamFlags.READWRITE)

            procedure.add_menu_path ('<Palettes>/Palettes Menu')

        return procedure

Gimp.main(PaletteToGradient.__gtype__, sys.argv)
