"""
Components/TapTargetView
========================

.. seealso::

    `TapTargetView, GitHub <https://github.com/KeepSafe/TapTargetView>`_

    `TapTargetView, Material archive <https://material.io/archive/guidelines/growth-communications/feature-discovery.html#>`_

.. rubric:: Provide value and improve engagement by introducing users to new
    features and functionality at relevant moments.

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-previous.gif
    :align: center

Usage
-----

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.taptargetview import MDTapTargetView

    KV = '''
    Screen:

        MDFloatingActionButton:
            id: button
            icon: "plus"
            pos: 10, 10
            on_release: app.tap_target_start()
    '''


    class TapTargetViewDemo(MDApp):
        def build(self):
            screen = Builder.load_string(KV)
            self.tap_target_view = MDTapTargetView(
                widget=screen.ids.button,
                title_text="This is an add button",
                description_text="This is a description of the button",
                widget_position="left_bottom",
            )

            return screen

        def tap_target_start(self):
            if self.tap_target_view.state == "close":
                self.tap_target_view.start()
            else:
                self.tap_target_view.stop()


    TapTargetViewDemo().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-usage.gif
    :align: center

Widget position
---------------

Sets the position of the widget relative to the floating circle.

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="right",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-right.png
    :align: center

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="left",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-left.png
    :align: center

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="top",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-top.png
    :align: center

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="bottom",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-bottom.png
    :align: center

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="left_top",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-left_top.png
    :align: center

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="right_top",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-right_top.png
    :align: center

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="left_bottom",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-left_bottom.png
    :align: center

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="right_bottom",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-right_bottom.png
    :align: center

If you use ``the widget_position = "center"`` parameter then you must
definitely specify the :attr:`~MDTapTargetView.title_position`.

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        widget_position="center",
        title_position="left_top",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-position-center.png
    :align: center

Text options
------------

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        title_text="Title text",
        description_text="Description text",
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-text.png
    :align: center


You can use the following options to control font size, color, and boldness:

- :attr:`~MDTapTargetView.title_text_size`
- :attr:`~MDTapTargetView.title_text_color`
- :attr:`~MDTapTargetView.title_text_bold`
- :attr:`~MDTapTargetView.description_text_size`
- :attr:`~MDTapTargetView.description_text_color`
- :attr:`~MDTapTargetView.description_text_bold`

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        title_text="Title text",
        title_text_size="36sp",
        description_text="Description text",
        description_text_color=[1, 0, 0, 1]
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-text-option.png
    :align: center

But you can also use markup to set these values.

.. code-block:: python

    self.tap_target_view = MDTapTargetView(
        ...
        title_text="[size=36]Title text[/size]",
        description_text="[color=#ff0000ff]Description text[/color]",
    )

Events control
--------------

.. code-block:: python

    self.tap_target_view.bind(on_open=self.on_open, on_close=self.on_close)

.. code-block:: python

    def on_open(self, instance_tap_target_view):
        '''Called at the time of the start of the widget opening animation.'''

        print("Open", instance_tap_target_view)

    def on_close(self, instance_tap_target_view):
        '''Called at the time of the start of the widget closed animation.'''

        print("Close", instance_tap_target_view)

.. Note:: See other parameters in the :class:`~MDTapTargetView` class.
"""

from kivy.animation import Animation
from kivy.event import EventDispatcher
from kivy.graphics import Color, Ellipse, Rectangle
from kivy.logger import Logger
from kivy.metrics import dp
from kivy.properties import (
    BooleanProperty,
    ListProperty,
    NumericProperty,
    ObjectProperty,
    OptionProperty,
    StringProperty,
)
from kivy.uix.label import Label

from kivymd.theming import ThemableBehavior


class MDTapTargetView(ThemableBehavior, EventDispatcher):
    """Rough try to mimic the working of Android's TapTargetView.

    :Events:
        :attr:`on_open`
            Called at the time of the start of the widget opening animation.
        :attr:`on_close`
            Called at the time of the start of the widget closed animation.
    """

    widget = ObjectProperty()
    """
    Widget to add ``TapTargetView`` upon.

    :attr:`widget` is an :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    outer_radius = NumericProperty(dp(200))
    """
    Radius for outer circle.

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-outer-radius.png
        :align: center

    :attr:`outer_radius` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `dp(200)`.
    """

    outer_circle_color = ListProperty()
    """
    Color for the outer circle in ``rgb`` format.

    .. code-block:: python

        self.tap_target_view = MDTapTargetView(
            ...
            outer_circle_color=(1, 0, 0)
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-outer-circle-color.png
        :align: center

    :attr:`outer_circle_color` is an :class:`~kivy.properties.ListProperty`
    and defaults to ``theme_cls.primary_color``.
    """

    outer_circle_alpha = NumericProperty(0.96)
    """
    Alpha value for outer circle.

    :attr:`outer_circle_alpha` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `0.96`.
    """

    target_radius = NumericProperty(dp(45))
    """
    Radius for target circle.

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-target-radius.png
        :align: center

    :attr:`target_radius` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `dp(45)`.
    """

    target_circle_color = ListProperty([1, 1, 1])
    """
    Color for target circle in ``rgb`` format.

    .. code-block:: python

        self.tap_target_view = MDTapTargetView(
            ...
            target_circle_color=(1, 0, 0)
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/tap-target-view-widget-target-circle-color.png
        :align: center

    :attr:`target_circle_color` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[1, 1, 1]`.
    """

    title_text = StringProperty()
    """
    Title to be shown on the view.

    :attr:`title_text` is an :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    title_text_size = NumericProperty(dp(25))
    """
    Text size for title.

    :attr:`title_text_size` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `dp(25)`.
    """

    title_text_color = ListProperty([1, 1, 1, 1])
    """
    Text color for title.

    :attr:`title_text_color` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[1, 1, 1, 1]`.
    """

    title_text_bold = BooleanProperty(True)
    """
    Whether title should be bold.

    :attr:`title_text_bold` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    description_text = StringProperty()
    """
    Description to be shown below the title (keep it short).

    :attr:`description_text` is an :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    description_text_size = NumericProperty(dp(20))
    """
    Text size for description text.

    :attr:`description_text_size` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `dp(20)`.
    """

    description_text_color = ListProperty([0.9, 0.9, 0.9, 1])
    """
    Text size for description text.

    :attr:`description_text_color` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0.9, 0.9, 0.9, 1]`.
    """

    description_text_bold = BooleanProperty(False)
    """
    Whether description should be bold.

    :attr:`description_text_bold` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    draw_shadow = BooleanProperty(False)
    """
    Whether to show shadow.

    :attr:`draw_shadow` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    cancelable = BooleanProperty(False)
    """
    Whether clicking outside the outer circle dismisses the view.

    :attr:`cancelable` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    widget_position = OptionProperty(
        "left",
        options=[
            "left",
            "right",
            "top",
            "bottom",
            "left_top",
            "right_top",
            "left_bottom",
            "right_bottom",
            "center",
        ],
    )
    """
    Sets the position of the widget on the :attr:`~outer_circle`. Available options are
    `'left`', `'right`', `'top`', `'bottom`', `'left_top`', `'right_top`',
    `'left_bottom`', `'right_bottom`', `'center`'.

    :attr:`widget_position` is an :class:`~kivy.properties.OptionProperty`
    and defaults to `'left'`.
    """

    title_position = OptionProperty(
        "auto",
        options=[
            "auto",
            "left",
            "right",
            "top",
            "bottom",
            "left_top",
            "right_top",
            "left_bottom",
            "right_bottom",
        ],
    )
    """
    Sets the position of :attr`~title_text` on the outer circle. Only works if
    :attr`~widget_position` is set to `'center'`. In all other cases, it
    calculates the :attr`~title_position` itself.
    Must be set to other than `'auto`' when :attr`~widget_position` is set
    to `'center`'.

    Available options are `'auto'`, `'left`', `'right`', `'top`', `'bottom`',
    `'left_top`', `'right_top`', `'left_bottom`', `'right_bottom`', `'center`'.

    :attr:`title_position` is an :class:`~kivy.properties.OptionProperty`
    and defaults to `'auto'`.
    """

    stop_on_outer_touch = BooleanProperty(False)
    """
    Whether clicking on outer circle stops the animation.

    :attr:`stop_on_outer_touch` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    stop_on_target_touch = BooleanProperty(True)
    """
    Whether clicking on target circle should stop the animation.

    :attr:`stop_on_target_touch` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    state = OptionProperty("close", options=["close", "open"])
    """
    State of :class:`~MDTapTargetView`.

    :attr:`state` is an :class:`~kivy.properties.OptionProperty`
    and defaults to `'close'`.
    """

    _outer_radius = NumericProperty(0)
    _target_radius = NumericProperty(0)

    __elevation = 0

    def __init__(self, **kwargs):
        self.ripple_max_dist = dp(90)
        self.on_outer_radius(self, self.outer_radius)
        self.on_target_radius(self, self.target_radius)
        self.anim_ripple = None

        self.core_title_text = Label(
            markup=True, size_hint=(None, None), bold=self.title_text_bold
        )
        self.core_title_text.bind(
            texture_size=self.core_title_text.setter("size")
        )
        self.core_description_text = Label(markup=True, size_hint=(None, None))
        self.core_description_text.bind(
            texture_size=self.core_description_text.setter("size")
        )

        super().__init__(**kwargs)
        self.register_event_type("on_outer_touch")
        self.register_event_type("on_target_touch")
        self.register_event_type("on_outside_click")
        self.register_event_type("on_open")
        self.register_event_type("on_close")

        if not self.outer_circle_color:
            self.outer_circle_color = self.theme_cls.primary_color[:-1]

    def start(self, *args):
        """Starts widget opening animation."""

        self._initialize()
        self._animate_outer()
        self.state = "open"
        self.core_title_text.opacity = 1
        self.core_description_text.opacity = 1
        self.dispatch("on_open")

        elevation = getattr(self.widget, "elevation", None)
        if elevation:
            self.__elevation = elevation
            self.widget.elevation = 0

    def stop(self, *args):
        """Starts widget close animation."""

        # It needs a better implementation.
        if self.anim_ripple is not None:
            self.anim_ripple.unbind(on_complete=self._repeat_ripple)
        self.core_title_text.opacity = 0
        self.core_description_text.opacity = 0
        anim = Animation(
            d=0.15,
            t="in_cubic",
            **dict(
                zip(
                    ["_outer_radius", "_target_radius", "target_ripple_radius"],
                    [0, 0, 0],
                )
            ),
        )
        anim.bind(on_complete=self._after_stop)
        anim.start(self.widget)

    def on_open(self, *args):
        """Called at the time of the start of the widget opening animation."""

    def on_close(self, *args):
        """Called at the time of the start of the widget closed animation."""

    def on_draw_shadow(self, instance, value):
        Logger.warning(
            "The shadow adding method will be implemented in future versions"
        )

    def on_description_text(self, instance, value):
        self.core_description_text.text = value

    def on_description_text_size(self, instance, value):
        self.core_description_text.font_size = value

    def on_description_text_bold(self, instance, value):
        self.core_description_text.bold = value

    def on_title_text(self, instance, value):
        self.core_title_text.text = value

    def on_title_text_size(self, instance, value):
        self.core_title_text.font_size = value

    def on_title_text_bold(self, instance, value):
        self.core_title_text.bold = value

    def on_outer_radius(self, instance, value):
        self._outer_radius = self.outer_radius * 2

    def on_target_radius(self, instance, value):
        self._target_radius = self.target_radius * 2

    def on_target_touch(self):
        if self.stop_on_target_touch:
            self.stop()

    def on_outer_touch(self):
        if self.stop_on_outer_touch:
            self.stop()

    def on_outside_click(self):
        if self.cancelable:
            self.stop()

    def _initialize(self):
        setattr(self.widget, "_outer_radius", 0)
        setattr(self.widget, "_target_radius", 0)
        setattr(self.widget, "target_ripple_radius", 0)
        setattr(self.widget, "target_ripple_alpha", 0)

        # Bind some function on widget event when this function is called
        # instead of when the class itself is initialized to prevent all
        # widgets of all instances to get bind at once and start messing up.
        self.widget.bind(on_touch_down=self._some_func)

    def _draw_canvas(self):
        _pos = self._ttv_pos()
        self.widget.canvas.before.remove_group("ttv_group")

        with self.widget.canvas.before:
            # Outer circle.
            Color(
                *self.outer_circle_color,
                self.outer_circle_alpha,
                group="ttv_group",
            )
            _rad1 = self.widget._outer_radius
            Ellipse(size=(_rad1, _rad1), pos=_pos[0], group="ttv_group")

            # Title text.
            Color(*self.title_text_color, group="ttv_group")
            Rectangle(
                size=self.core_title_text.texture.size,
                texture=self.core_title_text.texture,
                pos=_pos[1],
                group="ttv_group",
            )

            # Description text.
            Color(*self.description_text_color, group="ttv_group")
            Rectangle(
                size=self.core_description_text.texture.size,
                texture=self.core_description_text.texture,
                pos=(
                    _pos[1][0],
                    _pos[1][1] - self.core_description_text.size[1] - 5,
                ),
                group="ttv_group",
            )

            # Target circle.
            Color(*self.target_circle_color, group="ttv_group")
            _rad2 = self.widget._target_radius
            Ellipse(
                size=(_rad2, _rad2),
                pos=(
                    self.widget.x - (_rad2 / 2 - self.widget.size[0] / 2),
                    self.widget.y - (_rad2 / 2 - self.widget.size[0] / 2),
                ),
                group="ttv_group",
            )

            # Target ripple.
            Color(
                *self.target_circle_color,
                self.widget.target_ripple_alpha,
                group="ttv_group",
            )
            _rad3 = self.widget.target_ripple_radius
            Ellipse(
                size=(_rad3, _rad3),
                pos=(
                    self.widget.x - (_rad3 / 2 - self.widget.size[0] / 2),
                    self.widget.y - (_rad3 / 2 - self.widget.size[0] / 2),
                ),
                group="ttv_group",
            )

    def _after_stop(self, *args):
        self.widget.canvas.before.remove_group("ttv_group")
        args[0].stop_all(self.widget)

        elevation = getattr(self.widget, "elevation", None)
        if elevation:
            self.widget.elevation = self.__elevation

        self.dispatch("on_close")

        # Don't forget to unbind the function or it'll mess
        # up with other next bindings.
        self.widget.unbind(on_touch_down=self._some_func)
        self.state = "close"

    def _fix_elev(self):
        with self.widget.canvas.before:
            Color(a=self.widget._soft_shadow_a)
            Rectangle(
                texture=self.widget._soft_shadow_texture,
                size=self.widget._soft_shadow_size,
                pos=self.widget._soft_shadow_pos,
            )
            Color(a=self.widget._hard_shadow_a)
            Rectangle(
                texture=self.widget._hard_shadow_texture,
                size=self.widget._hard_shadow_size,
                pos=self.widget._hard_shadow_pos,
            )
            Color(a=1)

    def _animate_outer(self):
        anim = Animation(
            d=0.2,
            t="out_cubic",
            **dict(
                zip(
                    ["_outer_radius", "_target_radius"],
                    [self._outer_radius, self._target_radius],
                )
            ),
        )
        anim.cancel_all(self.widget)
        anim.bind(on_progress=lambda x, y, z: self._draw_canvas())
        anim.bind(on_complete=self._animate_ripple)
        anim.start(self.widget)
        setattr(self.widget, "target_ripple_radius", self._target_radius)
        setattr(self.widget, "target_ripple_alpha", 1)

    def _animate_ripple(self, *args):
        self.anim_ripple = Animation(
            d=1,
            t="in_cubic",
            target_ripple_radius=self._target_radius + self.ripple_max_dist,
            target_ripple_alpha=0,
        )
        self.anim_ripple.stop_all(self.widget)
        self.anim_ripple.bind(on_progress=lambda x, y, z: self._draw_canvas())
        self.anim_ripple.bind(on_complete=self._repeat_ripple)
        self.anim_ripple.start(self.widget)

    def _repeat_ripple(self, *args):
        setattr(self.widget, "target_ripple_radius", self._target_radius)
        setattr(self.widget, "target_ripple_alpha", 1)
        self._animate_ripple()

    def _some_func(self, wid, touch):
        """
        This function decides which one to dispatch based on the touch
        position.
        """

        if self._check_pos_target(touch.pos):
            self.dispatch("on_target_touch")
        elif self._check_pos_outer(touch.pos):
            self.dispatch("on_outer_touch")
        else:
            self.dispatch("on_outside_click")

    def _check_pos_outer(self, pos):
        """
        Checks if a given `pos` coordinate is within the :attr:`~outer_radius`.
        """

        cx = self.circ_pos[0] + self._outer_radius / 2
        cy = self.circ_pos[1] + self._outer_radius / 2
        r = self._outer_radius / 2
        h, k = pos

        lhs = (cx - h) ** 2 + (cy - k) ** 2
        rhs = r**2
        if lhs <= rhs:
            return True
        return False

    def _check_pos_target(self, pos):
        """
        Checks if a given `pos` coordinate is within the
        :attr:`~target_radius`.
        """

        cx = self.widget.pos[0] + self.widget.width / 2
        cy = self.widget.pos[1] + self.widget.height / 2
        r = self._target_radius / 2
        h, k = pos

        lhs = (cx - h) ** 2 + (cy - k) ** 2
        rhs = r**2
        if lhs <= rhs:
            return True
        return False

    def _ttv_pos(self):
        """
        Calculates the `pos` value for outer circle and text
        based on the position provided.

        :returns: A tuple containing pos for the circle and text.
        """

        _rad1 = self.widget._outer_radius
        _center_x = self.widget.x - (_rad1 / 2 - self.widget.size[0] / 2)
        _center_y = self.widget.y - (_rad1 / 2 - self.widget.size[0] / 2)

        if self.widget_position == "left":
            circ_pos = (_center_x + _rad1 / 3, _center_y)
            title_pos = (_center_x + _rad1 / 1.4, _center_y + _rad1 / 1.4)
        elif self.widget_position == "right":
            circ_pos = (_center_x - _rad1 / 3, _center_y)
            title_pos = (_center_x - _rad1 / 10, _center_y + _rad1 / 1.4)
        elif self.widget_position == "top":
            circ_pos = (_center_x, _center_y - _rad1 / 3)
            title_pos = (_center_x + _rad1 / 4, _center_y + _rad1 / 4)
        elif self.widget_position == "bottom":
            circ_pos = (_center_x, _center_y + _rad1 / 3)
            title_pos = (_center_x + _rad1 / 4, _center_y + _rad1 / 1.2)
        # Corner ones need to be at a little smaller distance
        # than edge ones that's why _rad1/4.
        elif self.widget_position == "left_top":
            circ_pos = (_center_x + _rad1 / 4, _center_y - _rad1 / 4)
            title_pos = (_center_x + _rad1 / 2, _center_y + _rad1 / 4)
        elif self.widget_position == "right_top":
            circ_pos = (_center_x - _rad1 / 4, _center_y - _rad1 / 4)
            title_pos = (_center_x - _rad1 / 10, _center_y + _rad1 / 4)
        elif self.widget_position == "left_bottom":
            circ_pos = (_center_x + _rad1 / 4, _center_y + _rad1 / 4)
            title_pos = (_center_x + _rad1 / 2, _center_y + _rad1 / 1.2)
        elif self.widget_position == "right_bottom":
            circ_pos = (_center_x - _rad1 / 4, _center_y + _rad1 / 4)
            title_pos = (_center_x, _center_y + _rad1 / 1.2)
        else:
            # Center.
            circ_pos = (_center_x, _center_y)

            if self.title_position == "auto":
                raise ValueError(
                    "widget_position='center' requires title_position to be set."
                )
            elif self.title_position == "left":
                title_pos = (_center_x + _rad1 / 10, _center_y + _rad1 / 2)
            elif self.title_position == "right":
                title_pos = (_center_x + _rad1 / 1.6, _center_y + _rad1 / 2)
            elif self.title_position == "top":
                title_pos = (_center_x + _rad1 / 2.5, _center_y + _rad1 / 1.3)
            elif self.title_position == "bottom":
                title_pos = (_center_x + _rad1 / 2.5, _center_y + _rad1 / 4)
            elif self.title_position == "left_top":
                title_pos = (_center_x + _rad1 / 8, _center_y + _rad1 / 1.4)
            elif self.title_position == "right_top":
                title_pos = (_center_x + _rad1 / 2, _center_y + _rad1 / 1.3)
            elif self.title_position == "left_bottom":
                title_pos = (_center_x + _rad1 / 8, _center_y + _rad1 / 4)
            elif self.title_position == "right_bottom":
                title_pos = (_center_x + _rad1 / 2, _center_y + _rad1 / 3.5)
            else:
                raise ValueError(
                    f"'{self.title_position}'"
                    f"is not a valid value for title_position"
                )

        self.circ_pos = circ_pos
        return circ_pos, title_pos