# Source code for psd_tools.api.effects

    """
    Effects module.
    """

    from __future__ import absolute_import, unicode_literals

    import logging

    from psd_tools.constants import Resource, Tag
    from psd_tools.psd.descriptor import Descriptor, List
    from psd_tools.terminology import Key, Klass
    from psd_tools.utils import new_registry

    logger = logging.getLogger(__name__)

    _TYPES, register = new_registry()

    class Effects(object):
        """
        List-like effects.
        """

        def __init__(self, layer):
            self._data = None
            for tag in (
                Tag.OBJECT_BASED_EFFECTS_LAYER_INFO,
                Tag.OBJECT_BASED_EFFECTS_LAYER_INFO_V0,
                Tag.OBJECT_BASED_EFFECTS_LAYER_INFO_V1,
            ):
                if tag in layer.tagged_blocks:
                    self._data = layer.tagged_blocks.get_data(tag)
                    break

            self._items = []
            for key in self._data or []:
                value = self._data[key]
                if not isinstance(value, List):
                    value = [value]
                for item in value:
                    if not (isinstance(item, Descriptor) and item.get(Key.Enabled)):
                        continue
                    kls = _TYPES.get(item.classID)
                    assert kls is not None, "kls not found for %r" % item.classID
                    self._items.append(kls(item, layer._psd.image_resources))

        @property
        def scale(self):
            """Scale value."""
            return self._data.get(Key.Scale).value if self._data else None

        @property
        def enabled(self):
            """Whether if all the effects are enabled.

            :rtype: bool
            """
            return bool(self._data.get(b"masterFXSwitch")) if self._data else False

        @property
        def items(self):
            return self._items

        def find(self, name):
            """Iterate effect items by name."""
            if not self.enabled:
                return
            KLASS = {kls.__name__.lower(): kls for kls in _TYPES.values()}
            for item in self:
                if isinstance(item, KLASS.get(name.lower(), None)):
                    yield item

        def __len__(self):
            return self._items.__len__()

        def __iter__(self):
            return self._items.__iter__()

        def __getitem__(self, key):
            return self._items.__getitem__(key)

        # def __setitem__(self, key, value):
        #     return self._items.__setitem__(key, value)

        # def __delitem__(self, key):
        #     return self._items.__delitem__(key)

        def __repr__(self):
            return "%s(%s)" % (
                self.__class__.__name__,
                " ".join(x.__class__.__name__.lower() for x in self) if self._data else "",
            )

    class _Effect(object):
        """Base Effect class."""

        def __init__(self, value, image_resources):
            self.value = value
            self._image_resources = image_resources

        @property
        def enabled(self):
            """Whether if the effect is enabled."""
            return bool(self.value.get(Key.Enabled))

        @property
        def present(self):
            """Whether if the effect is present in Photoshop UI."""
            return bool(self.value.get(b"present"))

        @property
        def shown(self):
            """Whether if the effect is shown in dialog."""
            return bool(self.value.get(b"showInDialog"))

        @property
        def opacity(self):
            """Layer effect opacity in percentage."""
            return self.value.get(Key.Opacity).value

        def __repr__(self):
            return self.__class__.__name__

        def _repr_pretty_(self, p, cycle):
            if cycle:
                return self.__repr__()
            p.text(self.__repr__())

    class _ColorMixin(object):
        @property
        def color(self):
            """Color."""
            return self.value.get(Key.Color)

        @property
        def blend_mode(self):
            """Effect blending mode."""
            return self.value.get(Key.Mode).enum

    class _ChokeNoiseMixin(_ColorMixin):
        @property
        def choke(self):
            """Choke level."""
            return self.value.get(Key.ChokeMatte).value

        @property
        def size(self):
            """Size in pixels."""
            return self.value.get(Key.Blur).value

        @property
        def noise(self):
            """Noise level."""
            return self.value.get(Key.Noise).value

        @property
        def anti_aliased(self):
            """Angi-aliased."""
            return bool(self.value.get(Key.AntiAlias))

        @property
        def contour(self):
            """Contour configuration."""
            return self.value.get(Key.TransferSpec)

    class _AngleMixin(object):
        @property
        def use_global_light(self):
            """Using global light."""
            return bool(self.value.get(Key.UseGlobalAngle))

        @property
        def angle(self):
            """Angle value."""
            if self.use_global_light:
                return self._image_resources.get_data(Resource.GLOBAL_ANGLE, 30.0)
            return self.value.get(Key.LocalLightingAngle).value

    class _GradientMixin(object):
        @property
        def gradient(self):
            """Gradient configuration."""
            return self.value.get(Key.Gradient)

        @property
        def angle(self):
            """Angle value."""
            return self.value.get(Key.Angle).value

        @property
        def type(self):
            """
            Gradient type, one of `linear`, `radial`, `angle`, `reflected`, or
            `diamond`.
            """
            return self.value.get(Key.Type).enum

        @property
        def reversed(self):
            """Reverse flag."""
            return bool(self.value.get(Key.Reverse))

        @property
        def dithered(self):
            """Dither flag."""
            return bool(self.value.get(Key.Dither))

        @property
        def offset(self):
            """Offset value."""
            return self.value.get(Key.Offset)

    class _PatternMixin(object):
        @property
        def pattern(self):
            """Pattern config."""
            # TODO: Expose nested property.
            return self.value.get(b"Ptrn")  # Enum.Pattern. Seems a bug.

        @property
        def linked(self):
            """Linked."""
            return self.value.get(b"Lnkd")  # Enum.Linked. Seems a bug.

        @property
        def angle(self):
            """Angle value."""
            return self.value.get(Key.Angle).value

        @property
        def phase(self):
            """Phase value in Point."""
            return self.value.get(b"phase")

    class _ShadowEffect(_Effect, _ChokeNoiseMixin, _AngleMixin):
        """Base class for shadow effect."""

        @property
        def distance(self):
            """Distance."""
            return self.value.get(Key.Distance).value

    class _GlowEffect(_Effect, _ChokeNoiseMixin, _GradientMixin):
        """Base class for glow effect."""

        @property
        def glow_type(self):
            """Glow type."""
            return self.value.get(Key.GlowTechnique).enum

        @property
        def quality_range(self):
            """Quality range."""
            return self.value.get(Key.InputRange).value

        @property
        def quality_jitter(self):
            """Quality jitter"""
            return self.value.get(Key.ShadingNoise).value

    class _OverlayEffect(_Effect):
        pass

    class _AlignScaleMixin(object):
        @property
        def blend_mode(self):
            """Effect blending mode."""
            return self.value.get(Key.Mode).enum

        @property
        def scale(self):
            """Scale value."""
            return self.value.get(Key.Scale).value

        @property
        def aligned(self):
            """Aligned."""
            return bool(self.value.get(Key.Alignment))

    @register(Klass.DropShadow.value)
    class DropShadow(_ShadowEffect):
        @property
        def layer_knocks_out(self):
            """Layers are knocking out."""
            return bool(self.value.get(b"layerConceals"))

    @register(Klass.InnerShadow.value)
    class InnerShadow(_ShadowEffect):
        pass

    @register(Klass.OuterGlow.value)
    class OuterGlow(_GlowEffect):
        @property
        def spread(self):
            return self.value.get(Key.ShadingNoise).value

    @register(Klass.InnerGlow.value)
    class InnerGlow(_GlowEffect):
        @property
        def glow_source(self):
            """Elements source."""
            return self.value.get(Key.InnerGlowSource).enum

    @register(Klass.SolidFill.value)
    class ColorOverlay(_OverlayEffect, _ColorMixin):
        pass

    @register(b"GrFl")  # Equal to Enum.GradientFill. This seems a bug.
    class GradientOverlay(_OverlayEffect, _AlignScaleMixin, _GradientMixin):
        pass

    @register(b"patternFill")
    class PatternOverlay(_OverlayEffect, _AlignScaleMixin, _PatternMixin):
        pass

    @register(Klass.FrameFX.value)
    class Stroke(_Effect, _ColorMixin, _PatternMixin, _GradientMixin):
        @property
        def position(self):
            """
            Position of the stroke, InsetFrame, OutsetFrame, or CenteredFrame.
            """
            return self.value.get(Key.Style).enum

        @property
        def fill_type(self):
            """Fill type, SolidColor, Gradient, or Pattern."""
            return self.value.get(Key.PaintType).enum

        @property
        def size(self):
            """Size value."""
            return self.value.get(Key.SizeKey).value

        @property
        def overprint(self):
            """Overprint flag."""
            return bool(self.value.get(b"overprint"))

    @register(Klass.BevelEmboss.value)
    class BevelEmboss(_Effect, _AngleMixin):
        @property
        def highlight_mode(self):
            """Highlight blending mode."""
            return self.value.get(Key.HighlightMode).enum

        @property
        def highlight_color(self):
            """Highlight color value."""
            return self.value.get(Key.HighlightColor)

        @property
        def highlight_opacity(self):
            """Highlight opacity value."""
            return self.value.get(Key.HighlightOpacity).value

        @property
        def shadow_mode(self):
            """Shadow blending mode."""
            return self.value.get(Key.ShadowMode).enum

        @property
        def shadow_color(self):
            """Shadow color value."""
            return self.value.get(Key.ShadowColor)

        @property
        def shadow_opacity(self):
            """Shadow opacity value."""
            return self.value.get(Key.ShadowOpacity).value

        @property
        def bevel_type(self):
            """Bevel type, one of `SoftMatte`, `HardLight`, `SoftLight`."""
            return self.value.get(Key.BevelTechnique).enum

        @property
        def bevel_style(self):
            """
            Bevel style, one of `OuterBevel`, `InnerBevel`, `Emboss`,
            `PillowEmboss`, or `StrokeEmboss`.
            """
            return self.value.get(Key.BevelStyle).enum

        @property
        def altitude(self):
            """Altitude value."""
            return self.value.get(Key.LocalLightingAltitude).value

        @property
        def depth(self):
            """Depth value."""
            return self.value.get(Key.StrengthRatio).value

        @property
        def size(self):
            """Size value in pixel."""
            return self.value.get(Key.Blur).value

        @property
        def direction(self):
            """Direction, either `StampIn` or `StampOut`."""
            return self.value.get(Key.BevelDirection).enum

        @property
        def contour(self):
            """Contour configuration."""
            return self.value.get(Key.TransferSpec)

        @property
        def anti_aliased(self):
            """Anti-aliased."""
            return bool(self.value.get(b"antialiasGloss"))

        @property
        def soften(self):
            """Soften value."""
            return self.value.get(Key.Softness).value

        @property
        def use_shape(self):
            """Using shape."""
            return bool(self.value.get(b"useShape"))

        @property
        def use_texture(self):
            """Using texture."""
            return bool(self.value.get(b"useTexture"))

    @register(Klass.ChromeFX.value)
    class Satin(_Effect, _ColorMixin):
        """Satin effect"""

        @property
        def anti_aliased(self):
            """Anti-aliased."""
            return bool(self.value.get(Key.AntiAlias))

        @property
        def inverted(self):
            """Inverted."""
            return bool(self.value.get(Key.Invert))

        @property
        def angle(self):
            """Angle value."""
            return self.value.get(Key.LocalLightingAngle).value

        @property
        def distance(self):
            """Distance value."""
            return self.value.get(Key.Distance).value

        @property
        def size(self):
            """Size value in pixel."""
            return self.value.get(Key.Blur).value

        @property
        def contour(self):
            """Contour configuration."""
            return self.value.get(Key.MappingShape)