#!/usr/bin/python
# -*- coding: UTF-8 -*-

__version__ = "1.0.0"

import pdb
import sys
import traceback

from kivy.animation import Animation
from kivy.app import App
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.effects.scroll import ScrollEffect
from kivy.effects.dampedscroll import DampedScrollEffect
from kivy.graphics.context_instructions import PopMatrix, PushMatrix, Rotate
from kivy.graphics.instructions import *
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.metrics import dp, sp
from kivy.properties import *
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.scatter import Scatter
from kivy.uix.scrollview import ScrollView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget

from pkg_resources import resource_filename

#KV Files
path = resource_filename( __name__, 'scroll.kv' )
Builder.load_file( path )

#Resources
spinner_image_default = resource_filename( __name__, 'spinner.png' )


class _RefreshScrollEffect( DampedScrollEffect ) :
    '''
    This class is simply based on DampedScrollEffect.
    If you need any documentation please look at kivy.effects.dampedscrolleffect.
    '''

    min_scroll_to_reload = NumericProperty( -dp(100) )
    '''
    Minimum overscroll value to reload.
    '''

    def on_overscroll( self, scrollview, overscroll ) :
        if overscroll < self.min_scroll_to_reload :
            scroll_view = self.target_widget.parent
            scroll_view._did_overscroll = True
            return True
        else : return False


class RefreshableScrollView( ScrollView ) :
    '''
    This is a very simple subclass of ScrollView.
    When the user does overscroll the view, a 'ReloadSpinner' is shown.
    You will need to call 'reload_done' once you've dove your loading.
    A ReloadSpinner widget will be added to the root_layout (you can choose ReloadSpinner class to use).
    '''

    on_start_reload = ObjectProperty( None )
    '''
    Will be called whenever overscroll occurs.
    '''
    
    spinner_class = StringProperty( 'LollipopSpinner' )
    '''
    Different class means a different effect.
    For a spinning image, for example, use ImageSpinner.
    By default class is 'LollipopSpinner', based on the latest android GMail app spinner.
    Class loading is done using eval, be careful.
    '''

    spinner_image = StringProperty( spinner_image_default )
    '''
    Set this property to change spinner appearance.
    '''
    
    spinner_diameter = NumericProperty( dp(38) )
    '''
    Size of the spinner
    '''

    spinner_duracy = NumericProperty( .2 )
    '''
    Spinner entrance\exit animation duracy
    '''
    
    root_layout = ObjectProperty( None )
    '''
    The spinner will be attached to this layout.
    ''' 

    spinner_speed = NumericProperty( 12 )
    '''
    Angle of rotation per-frame used by the spinner
    ''' 

    spinner_shadow_alpha = NumericProperty( .05 )
    '''
    Value of the shadow alpha channel.
    ''' 

    reload_spinner = ObjectProperty( None )
    '''
    Spinner widget reference used internally
    ''' 

    def __init__( self, **kargs ) :
        super( RefreshableScrollView, self ).__init__( **kargs )
        self.effect_cls = _RefreshScrollEffect
        self._reloading = False
        self._did_overscroll = False

    def on_touch_up( self, *args ) :

        if self._did_overscroll and not self._reloading :

            if self.on_start_reload : self.on_start_reload()
            self.reload_spinner = self._spinner_class()( 
                root_layout   = self.root_layout,
                spinner_image = self.spinner_image,
                shadow_alpha  = self.spinner_shadow_alpha,
                diameter      = self.spinner_diameter,
                duracy        = self.spinner_duracy,
                speed         = self.spinner_speed
            )
            self.reload_spinner.start()     
            self._reloading      = True
            self._did_overscroll = False
            return True
        
        return super( RefreshableScrollView, self ).on_touch_up( *args )

    def reload_done( self, *args ) :        
        self._reloading = False
        if self.reload_spinner : self.reload_spinner.stop()
    
    def _spinner_class( self ) :
        return eval( self.spinner_class ) 


class ReloadSpinner( Widget ) :
    '''
    Override this class if you want a custom spinner.
    There are fiew thigs to now :
        - Canvas is centered in the spinner center!
        - You need to PopMatrix when you're done drawing content
    See scroll.kv for more informations.
    '''

    spinner_image = StringProperty( 'spinner.png' )
    '''
    Set this property to change spinner appearance.
    '''

    shadow_alpha = NumericProperty( .05 )
    '''
    Value of the shadow alpha channel.
    ''' 

    root_layout = ObjectProperty( None )
    '''
    The spinner will be attached to this layout.
    ''' 
    
    diameter = NumericProperty( dp(48) )
    '''
    Size of the spinner.
    '''

    duracy = NumericProperty( .2 )
    '''
    Animation duracy.
    '''

    speed = NumericProperty( 6 )
    '''
    Angle of rotation increment.
    '''

    angle = NumericProperty( 0 )
    '''
    Current rotation.
    '''
    
    on_update_animation = ObjectProperty( None )
    '''
    Called after update_animation.
    '''

    def __init__( self, **kargs ) :
        super( ReloadSpinner, self ).__init__( **kargs )
    
    def update_animation( self, *args ) :
        self.angle += self.speed
        if self.angle >= 360 : self.angle = 0
        if self.on_update_animation : self.on_update_animation( self, *args )
    
    def start( self ) :

        self.pos = ( 
            self.root_layout.width/2 - self.width/2, 
            self.root_layout.height+self.height-dp(56)
        )

        animation = Animation( 
            y=self.root_layout.height-2*self.height, 
            duration=self.duracy
        )
        animation.start( self )         

        self.angle = 0
        self._hex = 0
        self._color = 0, 0, 0, 1 
        self.root_layout.add_widget( self )
        Clock.schedule_interval( self.update_animation, 0.04 )
    
    def stop( self ) : 
        animation = Animation( 
            y=self.root_layout.height-2*self.height, 
            duration=self.duracy
        )
        animation.bind(
            on_complete=self._remove_animation_done
        )
        animation.start( self )         

    def _remove_animation_done( self, *args ) :
        self.root_layout.remove_widget( self )
        Clock.unschedule( self.update_animation )


class ImageSpinner( ReloadSpinner ) :
    '''
    Based on the default spinner, kv lang provides image rendering.
    '''
    pass


class LollipopSpinner( ReloadSpinner ) :
    '''
    Based on the android gmail app spinner.
    '''

    color = ListProperty( [ 0, 0, 0, 1 ] )
    '''
    Current color of the inner arrow.
    '''

    angle2 = NumericProperty( 0 )
    '''
    Secon angle of rotation, will move slower.
    '''

    colors = ListProperty( [ [.051,.635,.376,1], [.867,.314,.267,1], [.227,.494,.953,1], [.969,.773,.253,1] ] )
    '''
    Colors to use.
    '''

    def __init__( self, **kargs ) :
        self._current_color = 0
        self.color = self.colors[ self._current_color ]     

        super( LollipopSpinner, self ).__init__( **kargs )
        self.speed = 12
        self.on_update_animation = self.update_angle2
        self.angle2 = 0

    def update_angle2( self, *args ) :
        self.angle2 -= self.speed
        if abs(self.angle2) == 360 : self.angle2 = 0

        if self.angle == self.angle2 == 0 :
            self._current_color += 1
            if self._current_color == len( self.colors ) : self._current_color = 0
            self.color = self.colors[ self._current_color ]