from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ListProperty, StringProperty, BooleanProperty
from kivymd.theming import ThemableBehavior
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.metrics import dp
Builder.load_string("""
<AKSpinnerBase>:
    size_hint: None, None

<AKSpinnerCircleFlip>:
    size: root.spinner_size , root.spinner_size

    canvas:
        Color:
            rgba: root.color if root.color else root.theme_cls.primary_color
        Ellipse:
            size:  root._circle_size
            pos: [ self.x+self.width/2 - root._circle_size[0]/2  , self.y+self.height/2 - root._circle_size[1]/2 ]

<AKSpinnerDoubleBounce>:
    size: root.spinner_size , root.spinner_size

    canvas:
        Color:
            rgba: root.color if root.color else root.theme_cls.primary_color
        Ellipse:
            size:  root._circle_size1
            pos: [ self.x+self.width/2 - root._circle_size1[0]/2  , self.y+self.height/2 - root._circle_size1[1]/2 ]

        Color:
            rgba: root.color_secondary if root.color_secondary else root.theme_cls.primary_light
        Ellipse:
            size:  root._circle_size2
            pos: [ self.x+self.width/2 - root._circle_size2[0]/2  , self.y+self.height/2 - root._circle_size2[1]/2 ]

<AKSpinnerFoldingCube>:
    size: root.spinner_size , root.spinner_size

    canvas:
        PushMatrix
        Rotate:
            axis: 0,0,1
            angle: root.angle
            origin: self.center
        Color:
            rgba: root.color if root.color else root.theme_cls.primary_color
            a:root._cube1a
        Rectangle:
            size: root._cubeitem1
            pos: [self.x, self.y]
        Color:
            rgba: root.color if root.color else root.theme_cls.primary_color
            a:root._cube2a
        Rectangle:
            size: root._cubeitem2
            pos: [self.x, self.y+self.height/2  ]
        Color:
            rgba: root.color if root.color else root.theme_cls.primary_color
            a:root._cube3a
        Rectangle:
            size: root._cubeitem3
            pos: [self.x+ self.width/2 , self.y+ self.height - root._cubeitem3[1]]
        Color:
            rgba: root.color if root.color else root.theme_cls.primary_color
            a:root._cube4a
        Rectangle:
            size: root._cubeitem4
            pos: [self.x+ self.width - root._cubeitem4[0] , self.y]
        PopMatrix

<AKSpinnerThreeDots>:
    size: root.spinner_size*3 , root.spinner_size
    BoxLayout:
        spacing: self.size[1]
        size_hint: None, None
        size: root.size
        pos: root.pos

        Widget:
            canvas:
                Color:
                    rgba: root.color if root.color else root.theme_cls.primary_color
                Ellipse:
                    size:  root._circle_size1
                    pos: [ self.x+self.width/2 - root._circle_size1[0]/2  , self.y+self.height/2 - root._circle_size1[1]/2 ]
        Widget:
            canvas:
                Color:
                    rgba: root.color if root.color else root.theme_cls.primary_color
                Ellipse:
                    size:  root._circle_size2
                    pos: [ self.x+self.width/2 - root._circle_size2[0]/2  , self.y+self.height/2 - root._circle_size2[1]/2 ]

        Widget:
            canvas:
                Color:
                    rgba: root.color if root.color else root.theme_cls.primary_color
                Ellipse:
                    size:  root._circle_size3
                    pos: [ self.x+self.width/2 - root._circle_size3[0]/2  , self.y+self.height/2 - root._circle_size3[1]/2 ]

""")


class AKSpinnerBase(ThemableBehavior, Widget):
    spinner_size = NumericProperty(48)
    speed = NumericProperty(0.4)
    active = BooleanProperty(False)
    color = ListProperty()


class AKSpinnerCircleFlip(AKSpinnerBase):
    animation = StringProperty('out_back')

    _circle_size = ListProperty([0, 0])

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def _start_animate(self, size):
        size = [size, size]
        self.flip_v_start = Animation(
            _circle_size=[
                size[0],
                size[1]],
            opacity=1,
            duration=self.speed,
            t=self.animation)
        self.flip_v = Animation(_circle_size=[size[0], 0], duration=self.speed, t=self.animation) \
            + Animation(_circle_size=[size[0], size[1]], duration=self.speed, t=self.animation)\
            + Animation(_circle_size=[0, size[1]], duration=self.speed, t=self.animation)\
            + Animation(_circle_size=[size[0], size[1]],
                        duration=self.speed, t=self.animation)

        self.flip_v.repeat = True
        self.flip_v_start.start(self)
        Clock.schedule_once(lambda x: self.flip_v.start(self), self.speed)

    def _stop_animate(self):
        self.flip_v_start.cancel_all(self)
        self.flip_v.cancel_all(self)
        self.flip_v_stop = Animation(
            _circle_size=[
                0,
                0],
            opacity=0,
            duration=0.1,
            t=self.animation)
        self.flip_v_stop.start(self)

    def on_active(self, *args):
        size = self.size[0]
        if self.active:
            self._start_animate(size)
        else:
            self._stop_animate()


class AKSpinnerDoubleBounce(AKSpinnerBase):
    color_secondary = ListProperty()

    _circle_size1 = ListProperty([0, 0])
    _circle_size2 = ListProperty([0, 0])

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def _update(self, size):
        self._circle_size1 = [size, size]
        self._circle_size2 = [0, 0]
        self._start_animate(size)

    def _start_animate(self, size):
        self.anim0 = Animation(
            _circle_size1=[
                size,
                size],
            opacity=1,
            t='out_quad',
            duration=self.speed)

        self.anim1 = Animation(_circle_size1=[size / 2, size / 2], t='in_quad', duration=self.speed)\
            + Animation(_circle_size1=[size, size],
                        t='out_quad', duration=self.speed)

        self.anim2 = Animation(_circle_size2=[size / 2, size / 2], opacity=1, t='in_quad', duration=self.speed)\
            + Animation(_circle_size2=[0, 0],
                        t='out_quad', duration=self.speed)

        self.anim1.repeat = True
        self.anim2.repeat = True

        self.anim0.start(self)
        Clock.schedule_once(lambda x: self.anim1.start(self), self.speed)
        Clock.schedule_once(lambda x: self.anim2.start(self), self.speed)

    def _stop_animate(self):
        self.anim0.cancel_all(self)
        self.anim1.cancel_all(self)
        self.anim2.cancel_all(self)
        self.anim1_stop = Animation(
            _circle_size1=[
                0,
                0],
            opacity=0,
            t='in_quad',
            duration=0.1)
        self.anim2_stop = Animation(
            _circle_size2=[
                0,
                0],
            opacity=0,
            t='in_quad',
            duration=0.1)
        self.anim1_stop.start(self)
        self.anim2_stop.start(self)

    def on_active(self, *args):
        size = self.size[0]
        if self.active:
            self._start_animate(size)
        else:
            self._stop_animate()


class AKSpinnerFoldingCube(AKSpinnerBase):
    angle = NumericProperty(45)
    animation = StringProperty('out_cubic')

    _cubeitem1 = ListProperty([0, 0])
    _cubeitem2 = ListProperty([0, 0])
    _cubeitem3 = ListProperty([0, 0])
    _cubeitem4 = ListProperty([0, 0])
    _cube1a = NumericProperty(0)
    _cube2a = NumericProperty(0)
    _cube3a = NumericProperty(0)
    _cube4a = NumericProperty(0)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def _start_animate(self, size):
        size /= 2
        self.cube_fold = Animation(_cubeitem1=[size, size], _cube1a=1, duration=self.speed, t=self.animation)\
            + Animation(_cubeitem2=[size, size], _cube2a=1, duration=self.speed, t=self.animation)\
            + Animation(_cubeitem3=[size, size], _cube3a=1, duration=self.speed, t=self.animation)\
            + Animation(_cubeitem4=[size, size], _cube4a=1, duration=self.speed, t=self.animation)\
            + Animation(_cubeitem4=[0, size], _cube4a=0, duration=self.speed, t=self.animation)\
            + Animation(_cubeitem3=[size, 0], _cube3a=0, duration=self.speed, t=self.animation)\
            + Animation(_cubeitem2=[0, size], _cube2a=0, duration=self.speed, t=self.animation)\
            + Animation(_cubeitem1=[size, 0], _cube1a=0, duration=self.speed, t=self.animation)\

        self.cube_fold.repeat = True
        self.cube_fold.start(self)

    def _update(self, size):
        self._cubeitem1 = [size / 2, 0]
        self._cubeitem2 = [0, size / 2]
        self._cubeitem3 = [size / 2, 0]
        self._cubeitem4 = [0, size / 2]

    def _stop_animate(self, size):
        size /= 2
        self.cube_fold.cancel_all(self)
        self.cube_stop = Animation(_cubeitem4=[0, size], _cube4a=0, duration=0.1, t=self.animation)\
            + Animation(_cubeitem3=[size, 0], _cube3a=0, duration=0.1, t=self.animation)\
            + Animation(_cubeitem2=[0, size], _cube2a=0, duration=0.1, t=self.animation)\
            + Animation(_cubeitem1=[size, 0], _cube1a=0, duration=0.1, t=self.animation)\

        self.cube_stop.start(self)

    def on_active(self, *args):
        size = self.size[0]
        self._update(size)
        if self.active:
            self._start_animate(size)
        else:
            self._stop_animate(size)


class AKSpinnerThreeDots(AKSpinnerBase):

    animation = StringProperty('linear')

    _circle_size1 = ListProperty([0, 0])
    _circle_size2 = ListProperty([0, 0])
    _circle_size3 = ListProperty([0, 0])

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def _start_animate(self, size):
        self.anim1 = Animation(_circle_size1=[size, size], opacity=1, duration=self.speed, t=self.animation)\
            + Animation(_circle_size1=[0, 0], duration=self.speed, t=self.animation)\
            + Animation(duration=self.speed)\

        self.anim2 = Animation(_circle_size2=[size, size], opacity=1, duration=self.speed, t=self.animation)\
            + Animation(_circle_size2=[0, 0], duration=self.speed, t=self.animation)\
            + Animation(duration=self.speed)\

        self.anim3 = Animation(_circle_size3=[size, size], opacity=1, duration=self.speed, t=self.animation)\
            + Animation(_circle_size3=[0, 0], duration=self.speed, t=self.animation)\
            + Animation(duration=self.speed)\

        self.anim1.repeat = True
        self.anim2.repeat = True
        self.anim3.repeat = True

        self.anim1.start(self)
        Clock.schedule_once(lambda dt: self.anim2.start(self), self.speed)
        Clock.schedule_once(lambda dt: self.anim3.start(self), self.speed * 2)

    def _stop_animate(self):
        self.anim1.cancel_all(self)
        self.anim2.cancel_all(self)
        self.anim3.cancel_all(self)
        self.anim1_stop = Animation(
            _circle_size1=[
                0,
                0],
            opacity=0,
            duration=0.1,
            t=self.animation)
        self.anim2_stop = Animation(
            _circle_size2=[
                0,
                0],
            opacity=0,
            duration=0.1,
            t=self.animation)
        self.anim3_stop = Animation(
            _circle_size3=[
                0,
                0],
            opacity=0,
            duration=0.1,
            t=self.animation)
        self.anim1_stop.start(self)
        self.anim2_stop.start(self)
        self.anim3_stop.start(self)

    def on_active(self, *args):
        size = self.size[1]
        if self.active:
            self._start_animate(size)
        else:
            self._stop_animate()
