#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# GIMP plugin to flatten a cylinder surface
# (c) Ofnuts 2023
#
#   History:
#
#   v0.0: 2024-07-20: First published version
#   v1.0: 2024-12-02: Convert to Gimp3

version='1.0'

#   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 2 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, write to the Free Software
#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import gi

gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('Gegl', '0.4')
from gi.repository import Gegl
gi.require_version('Babl', '0.1')
from gi.repository import Babl
from gi.repository import GObject

from gimphelpers import *
from collections.abc import Iterable
import sys

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



def onlyLayersOf(drawables: Iterable[Gimp.Drawable]):
    for l in drawables:
        if l.is_layer() and not l.is_group_layer():
            yield l

def colorName(layer: Gimp.Layer, color: Gegl.Color):
    r,g,b,_=color.get_rgba_with_space(Babl.space('sRGB'))
    return '#{1:02x}{2:02x}{3:02x} @ {0}'.format(layer.get_name(),*[int(round(x*255)) for x in (r,g,b)])

def createPath(image: Gimp.Image, scaledImage: Gimp.Image, layer: Gimp.Layer, color: Gegl.Color):
    pathName=colorName(layer,color)
    trace('Doing {}', pathName)
    scaledImage.select_color(Gimp.ChannelOps.REPLACE,layer,color)
    if Gimp.Selection.is_empty(scaledImage):
        Gimp.message(_('No pixels found for {0}').format(pathName))
        return
    # Other args of sel2path are default values
    res = callProcedure('plug-in-sel2path', image=scaledImage, drawables=[layer])
    scaledColorPath=scaledImage.get_paths()[0] # Always added at top
    colorPath = Gimp.Path.new(image, pathName)
    for strokeId in scaledColorPath.get_strokes():
        stype, points, closed = scaledColorPath.stroke_get_points(strokeId)
        scaledPoints = [round(p / 4) for p in points]
        colorPath.stroke_new_from_points(stype, scaledPoints, closed)
    image.insert_path(colorPath, None, 0)
    scaledImage.remove_path(scaledColorPath)

def colormapToPaths(procedure: Gimp.ImageProcedure,
                 run_mode: Gimp.RunMode,
                 image: Gimp.Image,
                 drawables: list[Gimp.Drawable],
                 config: Gimp.ProcedureConfig, data: Any):
    scaledImage=None
    selectableLayers=list(onlyLayersOf(drawables))
    if not selectableLayers:
        raise Exception(_('No plain layers in input'))
    scaledImage: Gimp.Image=image.duplicate()
    scaledImage.undo_disable()
    Gimp.context_set_interpolation(Gimp.InterpolationType.NONE)
    scaledImage.scale(scaledImage.get_width()*4,scaledImage.get_height()*4)
    colormap: Gimp.Palette=scaledImage.get_palette()
    colors: list[Gegl.Color]=colormap.get_colors()
    trace(f'Palette for {image.get_name()} contains {len(colors)} colors')
    Gimp.context_set_sample_criterion(Gimp.SelectCriterion.COMPOSITE)
    Gimp.context_set_sample_threshold(0.)
    Gimp.context_set_sample_merged(False)
    for layer in onlyLayersOf(scaledImage.get_selected_layers()):
        for color in colors:
            createPath(image,scaledImage,layer,color)
    if scaledImage:
        scaledImage.delete()
    return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())


class Ofn3ColormapToPaths(HelpedPlugin):
    author = 'Ofnuts'
    copyright = 'Ofnuts'
    year = '2024'
    procedures=[
        ProcedureDescription(
            'ofn3-colormap-to-paths',
            'INDEXED*',Gimp.ProcedureSensitivityMask.DRAWABLE|Gimp.ProcedureSensitivityMask.DRAWABLES,
            colormapToPaths,
            menuLabel=_('Colormap to paths'),
            menuPath='<Image>/'+_('Colors'),
            descShort=_('Create a selection path for each color in the colormap'),
            descLong=_('Create a selection path for each color in the colormap, for each selected layer'),
    ),
    ]

trace(f'*************** argv: {sys.argv}')
Gimp.main(Ofn3ColormapToPaths.__gtype__, sys.argv)
