#!/usr/pkg/bin/python3.12
# coding=utf-8
#
# Copyright (C) 2009 Aurelio A. Heckert, aurium (a) gmail dot 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 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
"""
Interpolation of attributes in selected objects or group's children.
"""

import inkex
from inkex.localization import inkex_gettext as _
from inkex.tween import ColorInterpolator, ValueInterpolator, AttributeInterpolator
from inkex.utils import is_number


class InterpAttG(inkex.EffectExtension):
    """
    This effect applies a value for any interpolatable attribute for all
    elements inside the selected group or for all elements in a multiple selection.
    """

    def __init__(self):
        super(InterpAttG, self).__init__()
        self.arg_parser.add_argument(
            "-a",
            "--att",
            type=str,
            dest="att",
            default="width",
            help="Attribute to be interpolated.",
        )
        self.arg_parser.add_argument(
            "-o",
            "--att-other",
            type=str,
            dest="att_other",
            help="Other attribute (for a limited UI).",
        )
        self.arg_parser.add_argument(
            "-t",
            "--att-other-type",
            type=self.arg_class([ColorInterpolator, ValueInterpolator]),
            dest="att_other_type",
            help="The other attribute type.",
        )
        self.arg_parser.add_argument(
            "-w",
            "--att-other-where",
            type=str,
            dest="att_other_where",
            default="tag",
            help="That is a tag attribute or a style attribute?",
        )
        self.arg_parser.add_argument(
            "-s",
            "--start-val",
            type=str,
            dest="start_val",
            default="#F00",
            help="Initial interpolation value.",
        )
        self.arg_parser.add_argument(
            "-e",
            "--end-val",
            type=str,
            dest="end_val",
            default="#00F",
            help="End interpolation value.",
        )
        self.arg_parser.add_argument(
            "-u", "--unit", type=str, dest="unit", default="none", help="Values unit."
        )
        self.arg_parser.add_argument(
            "--zsort",
            type=inkex.Boolean,
            dest="zsort",
            default=False,
            help="use z-order instead of selection order",
        )
        self.arg_parser.add_argument(
            "--tab",
            type=str,
            dest="tab",
            help="The selected UI-tab when OK was pressed",
        )

    def get_elements(self):
        """Returns a list of elements to work on"""
        if not self.svg.selection:
            return []

        if len(self.svg.selection) > 1:
            # multiple selection
            if self.options.zsort:
                return list(self.svg.selection.rendering_order().values())
            return list(self.svg.selection.values())

        # must be a group
        node = self.svg.selection.filter(inkex.Group).first()
        return list(node) or []

    def create_dummy_nodes(self, path):
        """Create dummy nodes to use the interpolation classes defined in inkex.tween"""
        pat1 = inkex.PathElement()
        pat2 = inkex.PathElement()

        start_value = self.options.start_val.replace(",", ".")
        end_value = self.options.end_val.replace(",", ".")
        if self.options.unit != "none":
            start_value += self.options.unit
            end_value += self.options.unit
        self.apply_value(pat1, path, start_value)
        self.apply_value(pat2, path, end_value)
        return pat1, pat2

    @staticmethod
    def apply_value(node, path, value):
        """Applies a value to a given node. If path starts with "transform/" or "style/", the
        value is applied to either transform or style."""
        if path.startswith("style/"):
            att_name = path[6:]
            node.style[att_name] = value
        elif path.startswith("transform/"):
            if not is_number(value):
                raise inkex.AbortExtension(
                    _("Unable to set attribute {} to {}").format(path, value)
                )
            if path == "transform/trans-x":
                node.transform.add_translate(value, 0)
            elif path == "transform/trans-y":
                node.transform.add_translate(0, value)
            elif path == "transform/scale":
                node.transform.add_scale(value)
        elif path == "transform":
            node.transform @= value
        else:
            node.set(path, value)

    def effect(self):
        method = None
        if self.options.att == "other":
            if self.options.att_other is None:
                raise inkex.AbortExtension(
                    _("You selected 'Other'. Please enter an attribute to interpolate.")
                )
            if self.options.att_other_where == "tag":
                path = self.options.att_other
            else:
                path = self.options.att_other_where + "/" + self.options.att_other
            method = self.options.att_other_type
        else:
            path = self.options.att
            if self.options.att == "height" or self.options.att == "width":
                method = inkex.tween.UnitValueInterpolator

        path1, path2 = self.create_dummy_nodes(path)
        if path.startswith("transform"):
            path = "transform"
        try:
            # maybe tween knows what do do with this attribute?
            interpolator = AttributeInterpolator.create_from_attribute(
                path1, path2, path, None
            )
        except:
            # okay, apparently not
            interpolator = AttributeInterpolator.create_from_attribute(
                path1, path2, path, method
            )
        collection = self.get_elements()
        if not collection:
            raise inkex.AbortExtension(_("There is no selection to interpolate"))

        steps = [1.0 / (len(collection) - 1) * i for i in range(len(collection))]

        for time, node in zip(steps, collection):
            new_value = interpolator.interpolate(time)
            InterpAttG.apply_value(node, path, new_value)

        return True


if __name__ == "__main__":
    InterpAttG().run()
